bug Fix 3712
[koha.git] / C4 / Search.pm
index 7c7cb7b..a468821 100644 (file)
@@ -19,7 +19,7 @@ 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;
@@ -27,6 +27,9 @@ use XML::Simple;
 use C4::Dates qw(format_date);
 use C4::XSLT;
 use C4::Branch;
+use C4::Debug;
+use YAML;
+use URI::Escape;
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG);
 
@@ -174,7 +177,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'};
@@ -633,10 +635,12 @@ 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
+                       $debug && warn "$_ Dump($operand)";
                        if ( my ($matched) = ($operand =~
-                               /(\P{IsAlnum}\Q$_\E\P{IsAlnum}|^\Q$_\E\P{IsAlnum}|\P{IsAlnum}\Q$_\E$|^\Q$_\E$)/gi) )
+                               /([^\X\p{isAlnum}]\Q$_\E[^\X\p{isAlnum}]|[^\X\p{isAlnum}]\Q$_\E$|^\Q$_\E[^\X\p{isAlnum}])/gi))
                        {
                                $operand =~ s/\Q$matched\E/ /gi;
                                push @stopwords_removed, $_;
@@ -772,6 +776,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,
@@ -808,9 +1000,10 @@ sub buildQuery {
 
     # no stemming/weight/fuzzy in NoZebra
     if ( C4::Context->preference("NoZebra") ) {
-        $stemming      = 0;
-        $weight_fields = 0;
-        $fuzzy_enabled = 0;
+        $stemming         = 0;
+        $weight_fields    = 0;
+        $fuzzy_enabled    = 0;
+       $auto_truncation  = 0;
     }
 
     my $query        = $operands[0];
@@ -827,6 +1020,17 @@ 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=/ ) {
@@ -901,6 +1105,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)/ ) {
@@ -910,16 +1119,6 @@ sub buildQuery {
                 # Some helpful index variants
                 my $index_plus       = $index . $struct_attr . ":" if $index;
                 my $index_plus_comma = $index . $struct_attr . "," if $index;
-                if ($auto_truncation){
-#                                      FIXME Auto Truncation is only valid for LTR languages
-#                                      use C4::Output;
-#                                      use C4::Languages qw(regex_lang_subtags get_bidi);
-#                              $lang = $query->cookie('KohaOpacLanguage') if (defined $query && $query->cookie('KohaOpacLanguage'));
-#                                  my $current_lang = regex_lang_subtags($lang);
-#                                  my $bidi;
-#                                  $bidi = get_bidi($current_lang->{script}) if $current_lang->{script};
-                                       $index_plus_comma .= "rtrn:";
-                               }
 
                 # Remove Stopwords
                 if ($remove_stopwords) {
@@ -930,6 +1129,16 @@ sub buildQuery {
                       if ( $stopwords_removed && $DEBUG );
                 }
 
+                if ($auto_truncation){
+                                       unless ( $index =~ /(st-|phr|ext)/ ) {
+                                               #FIXME only valid with LTR scripts
+                                               $operand=join(" ",map{ 
+                                                                                       (index($_,"*")>0?"$_":"$_*")
+                                                                                        }split (/\s+/,$operand));
+                                               warn $operand if $DEBUG;
+                                       }
+                               }
+
                 # Detect Truncation
                 my $truncated_operand;
                 my( $nontruncated, $righttruncated, $lefttruncated,
@@ -1127,23 +1336,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 <span>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'};
@@ -1196,12 +1395,28 @@ 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
@@ -1212,6 +1427,13 @@ sub searchResults {
         $aisbn =~ s/-//g;
         $oldbiblio->{amazonisbn} = $aisbn;
        $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} ) {
@@ -1246,48 +1468,6 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
             $oldbiblio->{summary} = $summary;
         }
 
-        # save an author with no <span> tag, for the <a href=search.pl?q=<!--tmpl_var name="author"-->> link
-        $oldbiblio->{'author_nospan'} = $oldbiblio->{'author'};
-        $oldbiblio->{'title_nospan'} = $oldbiblio->{'title'};
-        $oldbiblio->{'subtitle_nospan'} = $oldbiblio->{'subtitle'};
-        # Add search-term highlighting to the whole record where they match using <span>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 class=\"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 class=\"term\">$&<\/span>/gi;
-                               }
-            }
-        }
-
-        ($i % 2) and $oldbiblio->{'toggle'} = 1;
-
         # Pull out the items fields
         my @fields = $marcrecord->field($itemtag);
 
@@ -2107,6 +2287,96 @@ sub NZorder {
     }
 }
 
+=head2 enabled_staff_search_views
+
+%hash = enabled_staff_search_views()
+
+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<Output arg:>
+
+    * $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<usage in the script:>
+
+=back
+
+$template->param ( C4::Search::enabled_staff_search_views );
+
+=cut
+
+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
+       );
+}
+
+
+=head2 z3950_search_args
+
+$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<Output arg:>
+
+    * $array containing hash refs as described above
+
+=item C<usage in the script:>
+
+=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 $array;
+}
+
+
 END { }    # module clean-up code here (global destructor)
 
 1;