X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FSearch.pm;h=809bc87ea4108ab81339b69ce69b89668e96e6d6;hb=4a9cb3646a35fe41d83e6e983e9a13baa27dd59c;hp=f149434cccb19755a5b7c132745a5f63c3c16d82;hpb=8d29c78cbb2ed89057865528771e4683bc734201;p=koha.git diff --git a/C4/Search.pm b/C4/Search.pm index f149434ccc..809bc87ea4 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -16,15 +16,18 @@ package C4::Search; # Suite 330, Boston, MA 02111-1307 USA use strict; +# use warnings; # FIXME require Exporter; use C4::Context; -use C4::Biblio; # GetMarcFromKohaField +use C4::Biblio; # GetMarcFromKohaField, GetBiblioData use C4::Koha; # getFacets use Lingua::Stem; use C4::Search::PazPar2; use XML::Simple; use C4::Dates qw(format_date); use C4::XSLT; +use C4::Branch; +use URI::Escape; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG); @@ -52,44 +55,16 @@ This module provides searching functions for Koha's bibliographic databases @ISA = qw(Exporter); @EXPORT = qw( - &findseealso &FindDuplicate &SimpleSearch &searchResults &getRecords &buildQuery &NZgetRecords - &ModBiblios ); # make all your functions, whether exported or not; -=head2 findseealso($dbh,$fields); - -C<$dbh> is a link to the DB handler. - -use C4::Context; -my $dbh =C4::Context->dbh; - -C<$fields> is a reference to the fields array - -This function modifies the @$fields array and adds related fields to search on. - -FIXME: this function is probably deprecated in Koha 3 - -=cut - -sub findseealso { - my ( $dbh, $fields ) = @_; - my $tagslib = GetMarcStructure(1); - for ( my $i = 0 ; $i <= $#{$fields} ; $i++ ) { - my ($tag) = substr( @$fields[$i], 1, 3 ); - my ($subfield) = substr( @$fields[$i], 4, 1 ); - @$fields[$i] .= ',' . $tagslib->{$tag}->{$subfield}->{seealso} - if ( $tagslib->{$tag}->{$subfield}->{seealso} ); - } -} - =head2 FindDuplicate ($biblionumber,$biblionumber,$title) = FindDuplicate($record); @@ -200,7 +175,6 @@ for my $i (0..$hits) { my $biblio = TransformMarcToKoha(C4::Context->dbh,$marcrecord,''); #build the hash for the template. - $resultsloop{highlight} = ($i % 2)?(1):(0); $resultsloop{title} = $biblio->{'title'}; $resultsloop{subtitle} = $biblio->{'subtitle'}; $resultsloop{biblionumber} = $biblio->{'biblionumber'}; @@ -223,7 +197,7 @@ sub SimpleSearch { my $search_result = ( $result->{hits} && $result->{hits} > 0 ? $result->{'RECORDS'} : [] ); - return ( undef, $search_result, scalar($search_result) ); + return ( undef, $search_result, scalar($result->{hits}) ); } else { # FIXME hardcoded value. See catalog/search.pl & opac-search.pl too. @@ -346,40 +320,21 @@ sub getRecords { # 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] ) - ); - } - elsif ( $query_type =~ /^cql/ ) { - $results[$i] = - $zconns[$i]->search( - new ZOOM::Query::CQL( $query_to_use, $zconns[$i] ) ); - } - elsif ( $query_type =~ /^pqf/ ) { - $results[$i] = - $zconns[$i]->search( - new ZOOM::Query::PQF( $query_to_use, $zconns[$i] ) ); - } - } - else { - if ($scan) { - $results[$i] = - $zconns[$i]->scan( - new ZOOM::Query::CCL2RPN( $query_to_use, $zconns[$i] ) - ); - } - else { - $results[$i] = - $zconns[$i]->search( - new ZOOM::Query::CCL2RPN( $query_to_use, $zconns[$i] ) - ); + 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])); + } elsif ($query_type =~ /^cql/) { + $results[$i] = $zconns[$i]->search(new ZOOM::Query::CQL($query_to_use, $zconns[$i])); + } elsif ($query_type =~ /^pqf/) { + $results[$i] = $zconns[$i]->search(new ZOOM::Query::PQF($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])); + } else { + $results[$i] = $zconns[$i]->search(new ZOOM::Query::CCL2RPN($query_to_use, $zconns[$i])); } }; if ($@) { @@ -426,6 +381,9 @@ sub getRecords { elsif ( $sort eq "title_za" ) { $sort_by .= "1=4 >i "; } + else { + warn "Ignoring unrecognized sort '$sort' requested" if $sort_by; + } } if ($sort_by) { if ( $results[$i]->sort( "yaz", $sort_by ) < 0 ) { @@ -496,34 +454,20 @@ sub getRecords { #warn $servers[$i-1]."\n".$record; #.$facet_record->title(); if ($facet_record) { for ( my $k = 0 ; $k <= @$facets ; $k++ ) { - - if ( $facets->[$k] ) { - my @fields; - for my $tag ( @{ $facets->[$k]->{'tags'} } ) - { - push @fields, - $facet_record->field($tag); - } - for my $field (@fields) { - my @subfields = $field->subfields(); - for my $subfield (@subfields) { - my ( $code, $data ) = @$subfield; - if ( $code eq - $facets->[$k]->{'subfield'} ) - { - $facets_counter->{ $facets->[$k] - ->{'link_value'} } - ->{$data}++; - } - } + ($facets->[$k]) or next; + my @fields = map {$facet_record->field($_)} @{$facets->[$k]->{'tags'}} ; + for my $field (@fields) { + my @subfields = $field->subfields(); + for my $subfield (@subfields) { + my ( $code, $data ) = @$subfield; + ($code eq $facets->[$k]->{'subfield'}) or next; + $facets_counter->{ $facets->[$k]->{'link_value'} }->{$data}++; } - $facets_info->{ $facets->[$k] - ->{'link_value'} }->{'label_value'} = - $facets->[$k]->{'label_value'}; - $facets_info->{ $facets->[$k] - ->{'link_value'} }->{'expanded'} = - $facets->[$k]->{'expanded'}; } + $facets_info->{ $facets->[$k]->{'link_value'} }->{'label_value'} = + $facets->[$k]->{'label_value'}; + $facets_info->{ $facets->[$k]->{'link_value'} }->{'expanded'} = + $facets->[$k]->{'expanded'}; } } } @@ -538,15 +482,15 @@ sub getRecords { if ( $servers[ $i - 1 ] =~ /biblioserver/ ) { for my $link_value ( sort { $facets_counter->{$b} <=> $facets_counter->{$a} } - keys %$facets_counter ) + keys %$facets_counter ) { my $expandable; my $number_of_facets; my @this_facets_array; for my $one_facet ( sort { - $facets_counter->{$link_value} - ->{$b} <=> $facets_counter->{$link_value}->{$a} + $facets_counter->{$link_value}->{$b} + <=> $facets_counter->{$link_value}->{$a} } keys %{ $facets_counter->{$link_value} } ) { @@ -572,21 +516,14 @@ sub getRecords { $branches->{$one_facet}->{'branchname'}; } - # but we're down with the whole label being in the link's title. - my $facet_title_value = $one_facet; - - push @this_facets_array, - ( - { - facet_count => - $facets_counter->{$link_value} - ->{$one_facet}, - facet_label_value => $facet_label_value, - facet_title_value => $facet_title_value, - facet_link_value => $facet_link_value, - type_link_value => $link_value, - }, - ); + # 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, + }; } } @@ -596,17 +533,14 @@ sub getRecords { if ( ( $number_of_facets > 6 ) && ( $expanded_facet ne $link_value ) ); } - push @facets_loop, - ( - { - type_link_value => $link_value, - type_id => $link_value . "_id", - "type_label_" . $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')) ); + push @facets_loop, { + type_link_value => $link_value, + type_id => $link_value . "_id", + "type_label_" . $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')) ); } } } @@ -699,19 +633,16 @@ sub _remove_stopwords { # we use IsAlpha unicode definition, to deal correctly with diacritics. # otherwise, a French word like "leçon" woudl be split into "le" "çon", "le" # is a stopword, we'd get "çon" and wouldn't find anything... - foreach ( keys %{ C4::Context->stopwords } ) { - next if ( $_ =~ /(and|or|not)/ ); # don't remove operators - if ( $operand =~ - /(\P{IsAlpha}$_\P{IsAlpha}|^$_\P{IsAlpha}|\P{IsAlpha}$_$|^$_$)/ ) - { - $operand =~ s/\P{IsAlpha}$_\P{IsAlpha}/ /gi; - $operand =~ s/^$_\P{IsAlpha}/ /gi; - $operand =~ s/\P{IsAlpha}$_$/ /gi; - $operand =~ s/$1//gi; - push @stopwords_removed, $_; - } - } - } + foreach ( keys %{ C4::Context->stopwords } ) { + next if ( $_ =~ /(and|or|not)/ ); # don't remove operators + if ( my ($matched) = ($operand =~ + /(\P{IsAlnum}\Q$_\E\P{IsAlnum}|^\Q$_\E\P{IsAlnum}|\P{IsAlnum}\Q$_\E$|^\Q$_\E$)/gi) ) + { + $operand =~ s/\Q$matched\E/ /gi; + push @stopwords_removed, $_; + } + } + } return ( $operand, \@stopwords_removed ); } @@ -841,6 +772,194 @@ sub _build_weighted_query { return $weighted_query; } +=head2 getIndexes + +Return an array with available indexes. + +=cut + +sub getIndexes{ + my @indexes = ( + # biblio indexes + 'ab', + 'Abstract', + 'acqdate', + 'allrecords', + 'an', + 'Any', + 'at', + 'au', + 'aub', + 'aud', + 'audience', + 'auo', + 'aut', + 'Author', + 'Author-in-order ', + 'Author-personal-bibliography', + 'Authority-Number', + 'authtype', + 'bc', + 'biblionumber', + 'bio', + 'biography', + 'callnum', + 'cfn', + 'Chronological-subdivision', + 'cn-bib-source', + 'cn-bib-sort', + 'cn-class', + 'cn-item', + 'cn-prefix', + 'cn-suffix', + 'cpn', + 'Code-institution', + 'Conference-name', + 'Conference-name-heading', + 'Conference-name-see', + 'Conference-name-seealso', + 'Content-type', + 'Control-number', + 'copydate', + 'Corporate-name', + 'Corporate-name-heading', + 'Corporate-name-see', + 'Corporate-name-seealso', + 'ctype', + 'date-entered-on-file', + 'Date-of-acquisition', + 'Date-of-publication', + 'Dewey-classification', + 'extent', + 'fic', + 'fiction', + 'Form-subdivision', + 'format', + 'Geographic-subdivision', + 'he', + 'Heading', + 'Heading-use-main-or-added-entry', + 'Heading-use-series-added-entry ', + 'Heading-use-subject-added-entry', + 'Host-item', + 'id-other', + 'Illustration-code', + 'ISBN', + 'ISSN', + 'itemtype', + 'kw', + 'Koha-Auth-Number', + 'l-format', + 'language', + 'lc-card', + 'LC-card-number', + 'lcn', + 'llength', + 'ln', + 'Local-classification', + 'Local-number', + 'Match-heading', + 'Match-heading-see-from', + 'Material-type', + 'mc-itemtype', + 'mc-rtype', + 'mus', + 'Name-geographic', + 'Name-geographic-heading', + 'Name-geographic-see', + 'Name-geographic-seealso', + 'nb', + 'Note', + 'ns', + 'nt', + 'pb', + 'Personal-name', + 'Personal-name-heading', + 'Personal-name-see', + 'Personal-name-seealso', + 'pl', + 'Place-publication', + 'pn', + 'popularity', + 'pubdate', + 'Publisher', + 'Record-type', + 'rtype', + 'se', + 'See', + 'See-also', + 'sn', + 'Stock-number', + 'su', + 'Subject', + 'Subject-heading-thesaurus', + 'Subject-name-personal', + 'Subject-subdivision', + 'Summary', + 'Suppress', + 'su-geo', + 'su-na', + 'su-to', + 'su-ut', + 'ut', + 'Term-genre-form', + 'Term-genre-form-heading', + 'Term-genre-form-see', + 'Term-genre-form-seealso', + 'ti', + 'Title', + 'Title-cover', + 'Title-series', + 'Title-uniform', + 'Title-uniform-heading', + 'Title-uniform-see', + 'Title-uniform-seealso', + 'totalissues', + 'yr', + + # items indexes + 'acqsource', + 'barcode', + 'bc', + 'branch', + 'ccode', + 'classification-source', + 'cn-sort', + 'coded-location-qualifier', + 'copynumber', + 'damaged', + 'datelastborrowed', + 'datelastseen', + 'holdingbranch', + 'homebranch', + 'issues', + 'itemnumber', + 'itype', + 'Local-classification', + 'location', + 'lost', + 'materials-specified', + 'mc-ccode', + 'mc-itype', + 'mc-loc', + 'notforloan', + 'onloan', + 'price', + 'renewals', + 'replacementprice', + 'replacementpricedate', + 'reserves', + 'restricted', + 'stack', + 'uri', + 'withdrawn', + + # subject related + ); + + return \@indexes; +} + =head2 buildQuery ( $error, $query, @@ -863,11 +982,11 @@ sub buildQuery { warn "---------\nEnter buildQuery\n---------" if $DEBUG; # dereference - my @operators = @$operators if $operators; - my @indexes = @$indexes if $indexes; - my @operands = @$operands if $operands; - my @limits = @$limits if $limits; - my @sort_by = @$sort_by if $sort_by; + my @operators = $operators ? @$operators : (); + my @indexes = $indexes ? @$indexes : (); + my @operands = $operands ? @$operands : (); + my @limits = $limits ? @$limits : (); + my @sort_by = $sort_by ? @$sort_by : (); my $stemming = C4::Context->preference("QueryStemming") || 0; my $auto_truncation = C4::Context->preference("QueryAutoTruncate") || 0; @@ -896,16 +1015,27 @@ sub buildQuery { my $stopwords_removed; # flag to determine if stopwords have been removed + my $cclq; + my $cclindexes = getIndexes(); + if( $query !~ /\s*ccl=/ ){ + for my $index (@$cclindexes){ + if($query =~ /($index)(,?\w)*:/){ + $cclq = 1; + } + } + $query = "ccl=$query" if($cclq); + } + # for handling ccl, cql, pqf queries in diagnostic mode, skip the rest of the steps # DIAGNOSTIC ONLY!! if ( $query =~ /^ccl=/ ) { - return ( undef, $', $', $', $', '', '', '', '', 'ccl' ); + return ( undef, $', $', "q=ccl=$'", $', '', '', '', '', 'ccl' ); } if ( $query =~ /^cql=/ ) { - return ( undef, $', $', $', $', '', '', '', '', 'cql' ); + return ( undef, $', $', "q=cql=$'", $', '', '', '', '', 'cql' ); } if ( $query =~ /^pqf=/ ) { - return ( undef, $', $', $', $', '', '', '', '', 'pqf' ); + return ( undef, $', $', "q=pqf=$'", $', '', '', '', '', 'pqf' ); } # pass nested queries directly @@ -970,6 +1100,11 @@ sub buildQuery { ) = ( 0, 0, 0, 0, 0 ); } + + if(not $index){ + $index = 'kw'; + } + # Set default structure attribute (word list) my $struct_attr; unless ( $indexes_set || !$index || $index =~ /(st-|phr|ext|wrdl)/ ) { @@ -989,12 +1124,19 @@ sub buildQuery { if ( $stopwords_removed && $DEBUG ); } + if ($auto_truncation){ + unless ( $index =~ /(st-|phr|ext)/ ) { + #FIXME only valid with LTR scripts + $operand=join(" ",map{ + "$_*" + }split (/\s+/,$operand)); + warn $operand if $DEBUG; + } + } + # Detect Truncation - my ( $nontruncated, $righttruncated, $lefttruncated, - $rightlefttruncated, $regexpr ); my $truncated_operand; - ( - $nontruncated, $righttruncated, $lefttruncated, + my( $nontruncated, $righttruncated, $lefttruncated, $rightlefttruncated, $regexpr ) = _detect_truncation( $operand, $index ); warn @@ -1011,29 +1153,23 @@ sub buildQuery { $indexes_set = 1; undef $weight_fields; my $previous_truncation_operand; - if ( scalar(@$nontruncated) > 0 ) { + if (scalar @$nontruncated) { $truncated_operand .= "$index_plus @$nontruncated "; $previous_truncation_operand = 1; } - if ( scalar(@$righttruncated) > 0 ) { - $truncated_operand .= "and " - if $previous_truncation_operand; - $truncated_operand .= - "$index_plus_comma" . "rtrn:@$righttruncated "; + if (scalar @$righttruncated) { + $truncated_operand .= "and " if $previous_truncation_operand; + $truncated_operand .= $index_plus_comma . "rtrn:@$righttruncated "; $previous_truncation_operand = 1; } - if ( scalar(@$lefttruncated) > 0 ) { - $truncated_operand .= "and " - if $previous_truncation_operand; - $truncated_operand .= - "$index_plus_comma" . "ltrn:@$lefttruncated "; + if (scalar @$lefttruncated) { + $truncated_operand .= "and " if $previous_truncation_operand; + $truncated_operand .= $index_plus_comma . "ltrn:@$lefttruncated "; $previous_truncation_operand = 1; } - if ( scalar(@$rightlefttruncated) > 0 ) { - $truncated_operand .= "and " - if $previous_truncation_operand; - $truncated_operand .= - "$index_plus_comma" . "rltrn:@$rightlefttruncated "; + if (scalar @$rightlefttruncated) { + $truncated_operand .= "and " if $previous_truncation_operand; + $truncated_operand .= $index_plus_comma . "rltrn:@$rightlefttruncated "; $previous_truncation_operand = 1; } } @@ -1042,18 +1178,19 @@ sub buildQuery { # Handle Stemming my $stemmed_operand; - $stemmed_operand = _build_stemmed_operand($operand) - if $stemming; + $stemmed_operand = _build_stemmed_operand($operand) if $stemming; + warn "STEMMED OPERAND: >$stemmed_operand<" if $DEBUG; # Handle Field Weighting my $weighted_operand; - $weighted_operand = - _build_weighted_query( $operand, $stemmed_operand, $index ) - if $weight_fields; + 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; - $operand = $weighted_operand if $weight_fields; - $indexes_set = 1 if $weight_fields; # If there's a previous operand, we need to add an operator if ($previous_operand) { @@ -1124,12 +1261,20 @@ sub buildQuery { # Regular old limits else { - if ($this_limit){ - $limit .= " and " if $limit || $query; - $limit .= "$this_limit"; - $limit_cgi .= "&limit=$this_limit"; + $limit .= " and " if $limit || $query; + $limit .= "$this_limit"; + $limit_cgi .= "&limit=$this_limit"; + if ($this_limit =~ /^branch:(.+)/) { + my $branchcode = $1; + my $branchname = GetBranchName($branchcode); + if (defined $branchname) { + $limit_desc .= " branch:$branchname"; + } else { + $limit_desc .= " $this_limit"; + } + } else { $limit_desc .= " $this_limit"; - } + } } } if ($group_OR_limits) { @@ -1145,15 +1290,15 @@ sub buildQuery { $query =~ s/:/=/g; $limit =~ s/:/=/g; for ( $query, $query_desc, $limit, $limit_desc ) { - $_ =~ s/ / /g; # remove extra spaces - $_ =~ s/^ //g; # remove any beginning spaces - $_ =~ s/ $//g; # remove any ending spaces - $_ =~ s/==/=/g; # remove double == from query + s/ / /g; # remove extra spaces + s/^ //g; # remove any beginning spaces + s/ $//g; # remove any ending spaces + s/==/=/g; # remove double == from query } $query_cgi =~ s/^&//; # remove unnecessary & from beginning of the query cgi for ($query_cgi,$simple_query) { - $_ =~ s/"//g; + s/"//g; } # append the limit to the query $query .= " " . $limit; @@ -1186,23 +1331,13 @@ Format results in a form suitable for passing to the template sub searchResults { my ( $searchdesc, $hits, $results_per_page, $offset, $scan, @marcresults ) = @_; my $dbh = C4::Context->dbh; - my $even = 1; my @newresults; - # add search-term highlighting via s on the search terms - my $span_terms_hashref; - for my $span_term ( split( / /, $searchdesc ) ) { - $span_term =~ s/(.*=|\)|\(|\+|\.|\*)//g; - $span_terms_hashref->{$span_term}++; - } - #Build branchnames hash #find branchname #get branch information..... my %branches; - my $bsth = - $dbh->prepare("SELECT branchcode,branchname FROM branches") - ; # FIXME : use C4::Koha::GetBranches + 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'}; @@ -1255,22 +1390,45 @@ sub searchResults { else { $times = $hits; # FIXME: if $hits is undefined, why do we want to equal it? } + my $marcflavour = C4::Context->preference("marcflavour"); + # We get the biblionumber position in MARC + my ($bibliotag,$bibliosubf)=GetMarcFromKohaField('biblio.biblionumber',''); + my $fw = ''; + # loop through all of the records we've retrieved for ( my $i = $offset ; $i <= $times - 1 ; $i++ ) { my $marcrecord = MARC::File::USMARC::decode( $marcresults[$i] ); - my $oldbiblio = TransformMarcToKoha( $dbh, $marcrecord, '' ); - $oldbiblio->{subtitle} = C4::Biblio::get_koha_field_from_marc('bibliosubtitle', 'subtitle', $marcrecord, ''); + my $biblionumber; + + if(not $scan){ + if ($bibliotag<10){ + $biblionumber = $marcrecord->field($bibliotag)->data; + }else{ + $biblionumber = $marcrecord->subfield($bibliotag,$bibliosubf); + } + $fw = GetFrameworkCode($biblionumber); + } + + my $oldbiblio = TransformMarcToKoha( $dbh, $marcrecord, $fw ); + $oldbiblio->{subtitle} = GetRecordValue('subtitle', $marcrecord, $fw); $oldbiblio->{result_number} = $i + 1; # add imageurl to itemtype if there is one $oldbiblio->{imageurl} = getitemtypeimagelocation( 'opac', $itemtypes{ $oldbiblio->{itemtype} }->{imageurl} ); - $oldbiblio->{'authorised_value_images'} = C4::Items::get_authorised_value_images( C4::Biblio::get_biblio_authorised_values( $oldbiblio->{'biblionumber'} ) ); + $oldbiblio->{'authorised_value_images'} = C4::Items::get_authorised_value_images( C4::Biblio::get_biblio_authorised_values( $oldbiblio->{'biblionumber'}, $marcrecord ) ); (my $aisbn) = $oldbiblio->{isbn} =~ /([\d-]*[X]*)/; $aisbn =~ s/-//g; $oldbiblio->{amazonisbn} = $aisbn; - $oldbiblio->{description} = $itemtypes{ $oldbiblio->{itemtype} }->{description}; + $oldbiblio->{description} = $itemtypes{ $oldbiblio->{itemtype} }->{description}; + $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->{content_identifier_exists} = 1 if ($oldbiblio->{normalized_isbn} or $oldbiblio->{normalized_oclc} or $oldbiblio->{normalized_ean} or $oldbiblio->{normalized_upc}); + $oldbiblio->{edition} = $oldbiblio->{editionstatement}; + $oldbiblio->{description} = $itemtypes{ $oldbiblio->{itemtype} }->{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} ) { @@ -1279,6 +1437,10 @@ sub searchResults { foreach my $field (@fields) { my $tag = $field->tag(); my $tagvalue = $field->as_string(); + if (! utf8::is_utf8($tagvalue)) { + utf8::decode($tagvalue); + } + $summary =~ s/\[(.?.?.?.?)$tag\*(.*?)]/$1$tagvalue$2\[$1$tag$2]/g; unless ( $tag < 10 ) { @@ -1286,6 +1448,9 @@ sub searchResults { for my $i ( 0 .. $#subf ) { my $subfieldcode = $subf[$i][0]; my $subfieldvalue = $subf[$i][1]; + if (! utf8::is_utf8($subfieldvalue)) { + utf8::decode($subfieldvalue); + } my $tagsubf = $tag . $subfieldcode; $summary =~ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; @@ -1298,62 +1463,24 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; $oldbiblio->{summary} = $summary; } - # save an author with no tag, for the > link - $oldbiblio->{'author_nospan'} = $oldbiblio->{'author'}; - $oldbiblio->{'title_nospan'} = $oldbiblio->{'title'}; - # Add search-term highlighting to the whole record where they match using s - if (C4::Context->preference("OpacHighlightedWords")){ - my $searchhighlightblob; - for my $highlight_field ( $marcrecord->fields ) { - - # FIXME: need to skip title, subtitle, author, etc., as they are handled below - next if $highlight_field->tag() =~ /(^00)/; # skip fixed fields - for my $subfield ($highlight_field->subfields()) { - my $match; - next if $subfield->[0] eq '9'; - my $field = $subfield->[1]; - for my $term ( keys %$span_terms_hashref ) { - if ( ( $field =~ /$term/i ) && (( length($term) > 3 ) || ($field =~ / $term /i)) ) { - $field =~ s/$term/$&<\/span>/gi; - $match++; - } - } - $searchhighlightblob .= $field . " ... " if $match; - } - - } - $searchhighlightblob = ' ... '.$searchhighlightblob if $searchhighlightblob; - $oldbiblio->{'searchhighlightblob'} = $searchhighlightblob; - } - - # Add search-term highlighting to the title, subtitle, etc. fields - for my $term ( keys %$span_terms_hashref ) { - my $old_term = $term; - if ( length($term) > 3 ) { - $term =~ s/(.*=|\)|\(|\+|\.|\?|\[|\]|\\|\*)//g; - foreach(qw(title subtitle author publishercode place pages notes size)) { - $oldbiblio->{$_} =~ s/$term/$&<\/span>/gi; - } - } - } - - ($i % 2) and $oldbiblio->{'toggle'} = 1; - # Pull out the items fields my @fields = $marcrecord->field($itemtag); # Setting item statuses for display my @available_items_loop; my @onloan_items_loop; + my @notforloan_items_loop; my @other_items_loop; my $available_items; my $onloan_items; + my $notforloan_items; my $other_items; my $ordered_count = 0; my $available_count = 0; my $onloan_count = 0; + my $notforloan_count = 0; my $longoverdue_count = 0; my $other_count = 0; my $wthdrawn_count = 0; @@ -1363,7 +1490,6 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; my $item_in_transit_count = 0; my $can_place_holds = 0; my $items_count = scalar(@fields); - my $items_counter; my $maxitems = ( C4::Context->preference('maxItemsinSearchResults') ) ? C4::Context->preference('maxItemsinSearchResults') - 1 @@ -1372,7 +1498,6 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; # loop through every item foreach my $field (@fields) { my $item; - $items_counter++; # populate the items hash foreach my $code ( keys %subfieldstosearch ) { @@ -1387,18 +1512,21 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; elsif ($item->{$otherbranch}) { # Last resort $item->{'branchname'} = $branches{$item->{$otherbranch}}; } - + + ($item->{'reserved'}) = C4::Reserves::CheckReserves($item->{itemnumber}); + 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 - if ( $item->{onloan} ) { + if ( $item->{onloan} or $item->{reserved} ) { $onloan_count++; - my $key = $prefix . $item->{due_date}; + 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}->{imageurl} = getitemtypeimagelocation( 'opac', $itemtypes{ $item->{itype} }->{imageurl} ); + $onloan_items->{$key}->{barcode} = $item->{barcode}; # if something's checked out and lost, mark it as 'long overdue' if ( $item->{itemlost} ) { $onloan_items->{$prefix}->{longoverdue}++; @@ -1446,6 +1574,7 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; || $item->{itemlost} || $item->{damaged} || $item->{notforloan} + || $item->{reserved} || ($transfertwhen ne '')) { $wthdrawn_count++ if $item->{wthdrawn}; @@ -1453,24 +1582,43 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; $itemdamaged_count++ if $item->{damaged}; $item_in_transit_count++ if $transfertwhen ne ''; $item->{status} = $item->{wthdrawn} . "-" . $item->{itemlost} . "-" . $item->{damaged} . "-" . $item->{notforloan}; - $other_count++; my $key = $prefix . $item->{status}; + foreach (qw(wthdrawn itemlost damaged branchname itemcallnumber)) { - $other_items->{$key}->{$_} = $item->{$_}; + if($item->{notforloan} == 1){ + $notforloan_items->{$key}->{$_} = $item->{$_}; + }else{ + $other_items->{$key}->{$_} = $item->{$_}; + } } - $other_items->{$key}->{intransit} = ($transfertwhen ne '') ? 1 : 0; - $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value; - $other_items->{$key}->{count}++ if $item->{$hbranch}; - $other_items->{$key}->{location} = $shelflocations->{ $item->{location} }; - $other_items->{$key}->{imageurl} = getitemtypeimagelocation( 'opac', $itemtypes{ $item->{itype} }->{imageurl} ); + if($item->{notforloan} == 1){ + $notforloan_count++; + + $notforloan_items->{$key}->{intransit} = ($transfertwhen ne '') ? 1 : 0; + $notforloan_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value; + $notforloan_items->{$key}->{count}++ if $item->{$hbranch}; + $notforloan_items->{$key}->{location} = $shelflocations->{ $item->{location} }; + $notforloan_items->{$key}->{imageurl} = getitemtypeimagelocation( 'opac', $itemtypes{ $item->{itype} }->{imageurl} ); + $notforloan_items->{$key}->{barcode} = $item->{barcode}; + }else{ + $other_count++; + + $other_items->{$key}->{intransit} = ($transfertwhen ne '') ? 1 : 0; + $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value; + $other_items->{$key}->{count}++ if $item->{$hbranch}; + $other_items->{$key}->{location} = $shelflocations->{ $item->{location} }; + $other_items->{$key}->{imageurl} = getitemtypeimagelocation( 'opac', $itemtypes{ $item->{itype} }->{imageurl} ); + $other_items->{$key}->{barcode} = $item->{barcode}; + } + } # item is available else { $can_place_holds = 1; $available_count++; $available_items->{$prefix}->{count}++ if $item->{$hbranch}; - foreach (qw(branchname itemcallnumber)) { + foreach (qw(branchname itemcallnumber barcode)) { $available_items->{$prefix}->{$_} = $item->{$_}; } $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} }; @@ -1478,7 +1626,7 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; } } } # notforloan, item level and biblioitem level - my ( $availableitemscount, $onloanitemscount, $otheritemscount ); + my ( $availableitemscount, $onloanitemscount, $notforloanitemscount,$otheritemscount ); $maxitems = ( C4::Context->preference('maxItemsinSearchResults') ) ? C4::Context->preference('maxItemsinSearchResults') - 1 @@ -1491,6 +1639,10 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; (++$otheritemscount > $maxitems) and last; push @other_items_loop, $other_items->{$key}; } + for my $key ( sort keys %$notforloan_items ) { + (++$notforloanitemscount > $maxitems) and last; + push @notforloan_items_loop, $notforloan_items->{$key}; + } for my $key ( sort keys %$available_items ) { (++$availableitemscount > $maxitems) and last; push @available_items_loop, $available_items->{$key} @@ -1498,23 +1650,24 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g; # XSLT processing of some stuff if (C4::Context->preference("XSLTResultsDisplay") && !$scan) { - my $newxmlrecord = XSLTParse4Display($oldbiblio->{biblionumber},C4::Context->config('opachtdocs')."/prog/en/xslt/MARC21slim2OPACResults.xsl"); - $oldbiblio->{XSLTResultsRecord} = $newxmlrecord; + $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display( + $oldbiblio->{biblionumber}, $marcrecord, 'Results' ); } # last check for norequest : if itemtype is notforloan, it can't be reserved either, whatever the items - $can_place_holds = 0 - if $itemtypes{ $oldbiblio->{itemtype} }->{notforloan}; + $can_place_holds = 0 if $itemtypes{ $oldbiblio->{itemtype} }->{notforloan}; $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->{notforloan_items_loop}= \@notforloan_items_loop; $oldbiblio->{onloan_items_loop} = \@onloan_items_loop; $oldbiblio->{other_items_loop} = \@other_items_loop; $oldbiblio->{availablecount} = $available_count; $oldbiblio->{availableplural} = 1 if $available_count > 1; $oldbiblio->{onloancount} = $onloan_count; $oldbiblio->{onloanplural} = 1 if $onloan_count > 1; + $oldbiblio->{notforloancount} = $notforloan_count; $oldbiblio->{othercount} = $other_count; $oldbiblio->{otherplural} = 1 if $other_count > 1; $oldbiblio->{wthdrawncount} = $wthdrawn_count; @@ -1710,16 +1863,15 @@ sub NZanalyse { $left = 'subject' if $left =~ '^su$'; $left = 'koha-Auth-Number' if $left =~ '^an$'; $left = 'keyword' if $left =~ '^kw$'; + $left = 'itemtype' if $left =~ '^mc$'; # Fix for Bug 2599 - Search limits not working for NoZebra warn "handling leaf... left:$left operator:$operator right:$right" if $DEBUG; + my $dbh = C4::Context->dbh; if ( $operator && $left ne 'keyword' ) { - #do a specific search - my $dbh = C4::Context->dbh; $operator = 'LIKE' if $operator eq '=' and $right =~ /%/; - my $sth = - $dbh->prepare( + my $sth = $dbh->prepare( "SELECT biblionumbers,value FROM nozebra WHERE server=? AND indexname=? AND value $operator ?" - ); + ); warn "$left / $operator / $right\n" if $DEBUG; # split each word, query the DB and build the biblionumbers result @@ -1747,20 +1899,16 @@ sub NZanalyse { if ($results) { warn "NZAND" if $DEBUG; $results = NZoperatorAND($biblionumbers,$results); - } - else { + } else { $results = $biblionumbers; } } } else { - #do a complete search (all indexes), if index='kw' do complete search too. - my $dbh = C4::Context->dbh; - my $sth = - $dbh->prepare( + my $sth = $dbh->prepare( "SELECT biblionumbers FROM nozebra WHERE server=? AND value LIKE ?" - ); + ); # split each word, query the DB and build the biblionumbers result foreach ( split / /, $string ) { @@ -1873,7 +2021,7 @@ sub NZorder { my $popularity = $sth->fetchrow || 0; # hint : the key is popularity.title because we can have -# many results with the same popularity. In this cas, sub-ordering is done by title +# many results with the same popularity. In this case, sub-ordering is done by title # we also have biblionumber to avoid bug for 2 biblios with the same title & popularity # (un-frequent, I agree, but we won't forget anything that way ;-) $popularity{ sprintf( "%10d", $popularity ) . $title @@ -1954,9 +2102,9 @@ sub NZorder { my $record = GetMarcBiblio($biblionumber); my $callnumber; my ( $callnumber_tag, $callnumber_subfield ) = - GetMarcFromKohaField( $dbh, 'items.itemcallnumber' ); + GetMarcFromKohaField( 'items.itemcallnumber','' ); ( $callnumber_tag, $callnumber_subfield ) = - GetMarcFromKohaField('biblioitems.callnumber') + GetMarcFromKohaField('biblioitems.callnumber','') unless $callnumber_tag; if ( C4::Context->preference('marcflavour') eq 'UNIMARC' ) { $callnumber = $record->subfield( '200', 'f' ); @@ -2134,130 +2282,96 @@ sub NZorder { } } -=head2 ModBiblios +=head2 enabled_staff_search_views -($countchanged,$listunchanged) = ModBiblios($listbiblios, $tagsubfield,$initvalue,$targetvalue,$test); +%hash = enabled_staff_search_views() -this function changes all the values $initvalue in subfield $tag$subfield in any record in $listbiblios -test parameter if set donot perform change to records in database. +This function returns a hash that contains three flags obtained from the system +preferences, used to determine whether a particular staff search results view +is enabled. =over 2 -=item C - - * $listbiblios is an array ref to marcrecords to be changed - * $tagsubfield is the reference of the subfield to change. - * $initvalue is the value to search the record for - * $targetvalue is the value to set the subfield to - * $test is to be set only not to perform changes in database. - =item C - * $countchanged counts all the changes performed. - * $listunchanged contains the list of all the biblionumbers of records unchanged. + + * $hash{can_view_MARC} is true only if the MARC view is enabled + * $hash{can_view_ISBD} is true only if the ISBD view is enabled + * $hash{can_view_labeledMARC} is true only if the Labeled MARC view is enabled =item C =back -my ($countchanged, $listunchanged) = EditBiblios($results->{RECORD}, $tagsubfield,$initvalue,$targetvalue);; -#If one wants to display unchanged records, you should get biblios foreach @$listunchanged -$template->param(countchanged => $countchanged, loopunchanged=>$listunchanged); +$template->param ( C4::Search::enabled_staff_search_views ); =cut -sub ModBiblios { - my ( $listbiblios, $tagsubfield, $initvalue, $targetvalue, $test ) = @_; - my $countmatched; - my @unmatched; - my ( $tag, $subfield ) = ( $1, $2 ) - if ( $tagsubfield =~ /^(\d{1,3})([a-z0-9A-Z@])?$/ ); - if ( ( length($tag) < 3 ) && $subfield =~ /0-9/ ) { - $tag = $tag . $subfield; - undef $subfield; - } - my ( $bntag, $bnsubf ) = GetMarcFromKohaField('biblio.biblionumber'); - my ( $itemtag, $itemsubf ) = GetMarcFromKohaField('items.itemnumber'); - if ($tag eq $itemtag) { - # do not allow the embedded item tag to be - # edited from here - warn "Attempting to edit item tag via C4::Search::ModBiblios -- not allowed"; - return (0, []); - } - foreach my $usmarc (@$listbiblios) { - my $record; - $record = eval { MARC::Record->new_from_usmarc($usmarc) }; - my $biblionumber; - if ($@) { +sub enabled_staff_search_views +{ + return ( + can_view_MARC => C4::Context->preference('viewMARC'), # 1 if the staff search allows the MARC view + can_view_ISBD => C4::Context->preference('viewISBD'), # 1 if the staff search allows the ISBD view + can_view_labeledMARC => C4::Context->preference('viewLabeledMARC'), # 1 if the staff search allows the Labeled MARC view + ); +} - # usmarc is not a valid usmarc May be a biblionumber - # FIXME - sorry, please let's figure out whether - # this function is to be passed a list of - # record numbers or a list of MARC::Record - # objects. The former is probably better - # because the MARC records supplied by Zebra - # may be not current. - $record = GetMarcBiblio($usmarc); - $biblionumber = $usmarc; - } - else { - if ( $bntag >= 010 ) { - $biblionumber = $record->subfield( $bntag, $bnsubf ); - } - else { - $biblionumber = $record->field($bntag)->data; - } - } - #GetBiblionumber is to be written. - #Could be replaced by TransformMarcToKoha (But Would be longer) - if ( $record->field($tag) ) { - my $modify = 0; - foreach my $field ( $record->field($tag) ) { - if ($subfield) { - if ( - $field->delete_subfield( - 'code' => $subfield, - 'match' => qr($initvalue) - ) - ) - { - $countmatched++; - $modify = 1; - $field->update( $subfield, $targetvalue ) - if ($targetvalue); - } - } - else { - if ( $tag >= 010 ) { - if ( $field->delete_field($field) ) { - $countmatched++; - $modify = 1; - } - } - else { - $field->data = $targetvalue - if ( $field->data =~ qr($initvalue) ); - } - } - } +=head2 z3950_search_args - # warn $record->as_formatted; - if ($modify) { - ModBiblio( $record, $biblionumber, - GetFrameworkCode($biblionumber) ) - unless ($test); - } - else { - push @unmatched, $biblionumber; - } - } - else { - push @unmatched, $biblionumber; - } +$arrayref = z3950_search_args($matchpoints) + +This function returns an array reference that contains the search parameters to be +passed to the Z39.50 search script (z3950_search.pl). The array elements +are hash refs whose keys are name, value and encvalue, and whose values are the +name of a search parameter, the value of that search parameter and the URL encoded +value of that parameter. + +The search parameter names are lccn, isbn, issn, title, author, dewey and subject. + +The search parameter values are obtained from the bibliographic record whose +data is in a hash reference in $matchpoints, as returned by Biblio::GetBiblioData(). + +If $matchpoints is a scalar, it is assumed to be an unnamed query descriptor, e.g. +a general purpose search argument. In this case, the returned array contains only +entry: the key is 'title' and the value and encvalue are derived from $matchpoints. + +If a search parameter value is undefined or empty, it is not included in the returned +array. + +The returned array reference may be passed directly to the template parameters. + +=over 2 + +=item C + + * $array containing hash refs as described above + +=item C + +=back + +$data = Biblio::GetBiblioData($bibno); +$template->param ( MYLOOP => C4::Search::z3950_search_args($data) ) + +*OR* + +$template->param ( MYLOOP => C4::Search::z3950_search_args($searchscalar) ) + +=cut + +sub z3950_search_args { + my $bibrec = shift; + $bibrec = { title => $bibrec } if !ref $bibrec; + my $array = []; + for my $field (qw/ lccn isbn issn title author dewey subject /) + { + my $encvalue = URI::Escape::uri_escape_utf8($bibrec->{$field}); + push @$array, { name=>$field, value=>$bibrec->{$field}, encvalue=>$encvalue } if defined $bibrec->{$field}; } - return ( $countmatched, \@unmatched ); + return $array; } + END { } # module clean-up code here (global destructor) 1;