batchRebuildBiblioTables.pl doesn't crash anymore when GetMarcBiblio fails.
[koha.git] / C4 / Search.pm
index 1968622..3f4ee15 100644 (file)
@@ -16,12 +16,21 @@ 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 C4::Debug;
+use YAML;
+use URI::Escape;
+use C4::Charset;
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG);
 
@@ -49,44 +58,17 @@ This module provides searching functions for Koha's bibliographic databases
 
 @ISA    = qw(Exporter);
 @EXPORT = qw(
-  &findseealso
   &FindDuplicate
   &SimpleSearch
   &searchResults
+  &SearchAcquisitions
   &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);
@@ -155,7 +137,7 @@ sub FindDuplicate {
 
 =head2 SimpleSearch
 
-($error,$results) = SimpleSearch($query,@servers);
+( $error, $results, $total_hits ) = SimpleSearch( $query, $offset, $max_results, [@servers] );
 
 This function provides a simple search API on the bibliographic catalog
 
@@ -165,16 +147,21 @@ This function provides a simple search API on the bibliographic catalog
 
     * $query can be a simple keyword or a complete CCL query
     * @servers is optional. Defaults to biblioserver as found in koha-conf.xml
+    * $offset - If present, represents the number of records at the beggining to omit. Defaults to 0
+    * $max_results - if present, determines the maximum number of records to fetch. undef is All. defaults to undef.
+
+
+=item C<Output:>
 
-=item C<Output arg:>
     * $error is a empty unless an error is detected
     * \@results is an array of records.
+    * $total_hits is the number of hits that would have been returned with no limit
 
 =item C<usage in the script:>
 
 =back
 
-my ($error, $marcresults) = SimpleSearch($query);
+my ( $error, $marcresults, $total_hits ) = SimpleSearch($query);
 
 if (defined $error) {
     $template->param(query_error => $error);
@@ -186,13 +173,12 @@ if (defined $error) {
 my $hits = scalar @$marcresults;
 my @results;
 
-for(my $i=0;$i<$hits;$i++) {
+for my $i (0..$hits) {
     my %resultsloop;
     my $marcrecord = MARC::File::USMARC::decode($marcresults->[$i]);
     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'};
@@ -208,31 +194,31 @@ $template->param(result=>\@results);
 =cut
 
 sub SimpleSearch {
-    my $query = shift;
+    my ( $query, $offset, $max_results, $servers )  = @_;
+    
     if ( C4::Context->preference('NoZebra') ) {
         my $result = NZorder( NZanalyse($query) )->{'biblioserver'};
         my $search_result =
           (      $result->{hits}
               && $result->{hits} > 0 ? $result->{'RECORDS'} : [] );
-        return ( undef, $search_result );
+        return ( undef, $search_result, scalar($result->{hits}) );
     }
     else {
-        my @servers = @_;
+        # FIXME hardcoded value. See catalog/search.pl & opac-search.pl too.
+        my @servers = defined ( $servers ) ? @$servers : ( "biblioserver" );
         my @results;
+        my @zoom_queries;
         my @tmpresults;
         my @zconns;
-        return ( "No query entered", undef ) unless $query;
-
-        # FIXME hardcoded value. See catalog/search.pl & opac-search.pl too.
-        @servers = ("biblioserver") unless @servers;
+        my $total_hits;
+        return ( "No query entered", undef, undef ) unless $query;
 
         # Initialize & Search Zebra
         for ( my $i = 0 ; $i < @servers ; $i++ ) {
             eval {
                 $zconns[$i] = C4::Context->Zconn( $servers[$i], 1 );
-                $tmpresults[$i] =
-                  $zconns[$i]
-                  ->search( new ZOOM::Query::CCL2RPN( $query, $zconns[$i] ) );
+                $zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]);
+                $tmpresults[$i] = $zconns[$i]->search( $zoom_queries[$i] );
 
                 # error handling
                 my $error =
@@ -241,7 +227,7 @@ sub SimpleSearch {
                   . $zconns[$i]->addinfo() . " "
                   . $zconns[$i]->diagset();
 
-                return ( $error, undef ) if $zconns[$i]->errcode();
+                return ( $error, undef, undef ) if $zconns[$i]->errcode();
             };
             if ($@) {
 
@@ -252,25 +238,36 @@ sub SimpleSearch {
                   . $@->addinfo() . " "
                   . $@->diagset();
                 warn $error;
-                return ( $error, undef );
+                return ( $error, undef, undef );
             }
         }
-        my $hits = 0;
-        my $ev;
         while ( ( my $i = ZOOM::event( \@zconns ) ) != 0 ) {
-            $ev = $zconns[ $i - 1 ]->last_event();
-            if ( $ev == ZOOM::Event::ZEND ) {
-                $hits = $tmpresults[ $i - 1 ]->size();
-            }
-            if ( $hits > 0 ) {
-                for ( my $j = 0 ; $j < $hits ; $j++ ) {
-                    my $record = $tmpresults[ $i - 1 ]->record($j)->raw();
+            my $event = $zconns[ $i - 1 ]->last_event();
+            if ( $event == ZOOM::Event::ZEND ) {
+
+                my $first_record = defined( $offset ) ? $offset+1 : 1;
+                my $hits = $tmpresults[ $i - 1 ]->size();
+                $total_hits += $hits;
+                my $last_record = $hits;
+                if ( defined $max_results && $offset + $max_results < $hits ) {
+                    $last_record  = $offset + $max_results;
+                }
+
+                for my $j ( $first_record..$last_record ) {
+                    my $record = $tmpresults[ $i - 1 ]->record( $j-1 )->raw(); # 0 indexed
                     push @results, $record;
                 }
             }
-            $hits = 0;
         }
-        return ( undef, \@results );
+
+        foreach my $result (@tmpresults) {
+            $result->destroy();
+        }
+        foreach my $zoom_query (@zoom_queries) {
+            $zoom_query->destroy();
+        }
+
+        return ( undef, \@results, $total_hits );
     }
 }
 
@@ -320,53 +317,28 @@ sub getRecords {
 
 # perform the search, create the results objects
 # if this is a local search, use the $koha-query, if it's a federated one, use the federated-query
-        my $query_to_use;
-        if ( $servers[$i] =~ /biblioserver/ ) {
-            $query_to_use = $koha_query;
-        }
-        else {
-            $query_to_use = $simple_query;
-        }
+        my $query_to_use = ($servers[$i] =~ /biblioserver/) ? $koha_query : $simple_query;
 
         #$query_to_use = $simple_query if $scan;
         warn $simple_query if ( $scan and $DEBUG );
 
         # 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 ($@) {
@@ -413,6 +385,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 ) {
@@ -457,30 +432,22 @@ sub getRecords {
                         my $tmpauthor;
 
                 # the minimal record in author/title (depending on MARC flavour)
-                        if ( C4::Context->preference("marcflavour") eq
-                            "UNIMARC" )
-                        {
-                            $tmptitle = MARC::Field->new(
-                                '200', ' ', ' ',
-                                a => $term,
-                                f => $occ
-                            );
+                        if (C4::Context->preference("marcflavour") eq "UNIMARC") {
+                            $tmptitle = MARC::Field->new('200',' ',' ', a => $term, f => $occ);
+                            $tmprecord->append_fields($tmptitle);
+                        } else {
+                            $tmptitle  = MARC::Field->new('245',' ',' ', a => $term,);
+                            $tmpauthor = MARC::Field->new('100',' ',' ', a => $occ,);
+                            $tmprecord->append_fields($tmptitle);
+                            $tmprecord->append_fields($tmpauthor);
                         }
-                        else {
-                            $tmptitle =
-                              MARC::Field->new( '245', ' ', ' ', a => $term, );
-                            $tmpauthor =
-                              MARC::Field->new( '100', ' ', ' ', a => $occ, );
-                        }
-                        $tmprecord->append_fields($tmptitle);
-                        $tmprecord->append_fields($tmpauthor);
-                        $results_hash->{'RECORDS'}[$j] =
-                          $tmprecord->as_usmarc();
+                        $results_hash->{'RECORDS'}[$j] = $tmprecord->as_usmarc();
                     }
 
                     # not an index scan
                     else {
                         $record = $results[ $i - 1 ]->record($j)->raw();
+                       warn $results[$i-1]->record($j)->render() ;
 
                         # warn "RECORD $j:".$record;
                         $results_hash->{'RECORDS'}[$j] = $record;
@@ -492,34 +459,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);
+                                ($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}++;
                                     }
-                                    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_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'};
                             }
                         }
                     }
@@ -534,15 +487,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} }
                       )
                     {
@@ -568,21 +521,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,
+                            };
                         }
                     }
 
@@ -592,18 +538,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'},
-                            facets     => \@this_facets_array,
-                            expandable => $expandable,
-                            expand     => $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')) );
                 }
             }
         }
@@ -611,9 +553,6 @@ sub getRecords {
     return ( undef, $results_hashref, \@facets_loop );
 }
 
-use C4::Search::PazPar2;
-use XML::Simple;
-use Data::Dumper;
 sub pazGetRecords {
     my (
         $koha_query,       $simple_query, $sort_by_ref,    $servers_ref,
@@ -621,7 +560,7 @@ sub pazGetRecords {
         $query_type,       $scan
     ) = @_;
 
-    my $paz = C4::Search::PazPar2->new('http://localhost:10006/search.pz2');
+    my $paz = C4::Search::PazPar2->new(C4::Context->config('pazpar2url'));
     $paz->init();
     $paz->search($simple_query);
     sleep 1;
@@ -629,48 +568,60 @@ sub pazGetRecords {
     # do results
     my $results_hashref = {};
     my $stats = XMLin($paz->stat);
-    $results_hashref->{'biblioserver'}->{'hits'} = $stats->{'hits'};
-    my $results = XMLin($paz->show($offset, $results_per_page), forcearray => 1);
-    #die Dumper($results);
+    my $results = XMLin($paz->show($offset, $results_per_page, 'work-title:1'), forcearray => 1);
+   
+    # for a grouped search result, the number of hits
+    # is the number of groups returned; 'bib_hits' will have
+    # the total number of bibs. 
+    $results_hashref->{'biblioserver'}->{'hits'} = $results->{'merged'}->[0];
+    $results_hashref->{'biblioserver'}->{'bib_hits'} = $stats->{'hits'};
+
     HIT: foreach my $hit (@{ $results->{'hit'} }) {
-        warn "hit";
         my $recid = $hit->{recid}->[0];
-        #if ($recid =~ /[\200-\377]/) {
-        if ($recid =~ /sodot/) {
-            #die "bad $recid\n";
-            #probably do not want non-ASCII in record ID
-            last HIT;
+
+        my $work_title = $hit->{'md-work-title'}->[0];
+        my $work_author;
+        if (exists $hit->{'md-work-author'}) {
+            $work_author = $hit->{'md-work-author'}->[0];
         }
+        my $group_label = (defined $work_author) ? "$work_title / $work_author" : $work_title;
+
+        my $result_group = {};
+        $result_group->{'group_label'} = $group_label;
+        $result_group->{'group_merge_key'} = $recid;
+
         my $count = 1;
         if (exists $hit->{count}) {
             $count = $hit->{count}->[0];
         }
-        #die $count;
+        $result_group->{'group_count'} = $count;
+
         for (my $i = 0; $i < $count; $i++) {
-            warn "look for $recid offset = $i";
+            # FIXME -- may need to worry about diacritics here
             my $rec = $paz->record($recid, $i);
-            warn "got record $i";
-            push @{ $results_hashref->{'biblioserver'}->{'RECORDS'} }, $paz->record($recid, $i);
+            push @{ $result_group->{'RECORDS'} }, $rec;
         }
+
+        push @{ $results_hashref->{'biblioserver'}->{'GROUPS'} }, $result_group;
     }
-    warn "past hits";
     
     # pass through facets
     my $termlist_xml = $paz->termlist('author,subject');
     my $terms = XMLin($termlist_xml, forcearray => 1);
     my @facets_loop = ();
-    foreach my $list (sort keys %{ $terms->{'list'} }) {
-        my @facets = ();
-        foreach my $facet (sort @{ $terms->{'list'}->{$list}->{'term'} } ) {
-            push @facets, {
-                facet_label_value => $facet->{'name'}->[0],
-            };
-        }
-        push @facets_loop, ( {
-            type_label => $list,
-            facets => \@facets,
-        } );
-    }
+    #die Dumper($results);
+#    foreach my $list (sort keys %{ $terms->{'list'} }) {
+#        my @facets = ();
+#        foreach my $facet (sort @{ $terms->{'list'}->{$list}->{'term'} } ) {
+#            push @facets, {
+#                facet_label_value => $facet->{'name'}->[0],
+#            };
+#        }
+#        push @facets_loop, ( {
+#            type_label => $list,
+#            facets => \@facets,
+#        } );
+#    }
 
     return ( undef, $results_hashref, \@facets_loop );
 }
@@ -687,18 +638,18 @@ 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;
-                push @stopwords_removed, $_;
-            }
-        }
-    }
+#       
+               foreach ( keys %{ C4::Context->stopwords } ) {
+                       next if ( $_ =~ /(and|or|not)/ );    # don't remove operators
+                       $debug && warn "$_ Dump($operand)";
+                       if ( my ($matched) = ($operand =~
+                               /([^\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, $_;
+                       }
+               }
+       }
     return ( $operand, \@stopwords_removed );
 }
 
@@ -737,6 +688,13 @@ sub _build_stemmed_operand {
     my ($operand) = @_;
     my $stemmed_operand;
 
+    # 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 
+    # "014100018X" to "x ", which for a MARC21 database would bring up irrelevant
+    # results (e.g., "23 x 29 cm." from the 300$c).  Bug 2098.
+    return $operand if $operand =~ /\d/;
+
 # FIXME: the locale should be set based on the user's language and/or search choice
     my $stemmer = Lingua::Stem->new( -locale => 'EN-US' );
 
@@ -821,6 +779,195 @@ 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',
+                    'item',
+                    '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,
@@ -840,16 +987,14 @@ See verbose embedded documentation.
 sub buildQuery {
     my ( $operators, $operands, $indexes, $limits, $sort_by, $scan ) = @_;
 
-    warn "---------"        if $DEBUG;
-    warn "Enter buildQuery" if $DEBUG;
-    warn "---------"        if $DEBUG;
+    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;
@@ -859,9 +1004,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];
@@ -878,16 +1024,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
@@ -932,28 +1089,34 @@ sub buildQuery {
                 # Date of Publication
                 if ( $index eq 'yr' ) {
                     $index .= ",st-numeric";
-                    $indexes_set++;
-                    (
-                        $stemming,      $auto_truncation,
-                        $weight_fields, $fuzzy_enabled,
-                        $remove_stopwords
-                    ) = ( 0, 0, 0, 0, 0 );
+#                     $indexes_set++;
+                                       $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = $remove_stopwords = 0;
                 }
 
                 # Date of Acquisition
                 elsif ( $index eq 'acqdate' ) {
                     $index .= ",st-date-normalized";
-                    $indexes_set++;
-                    (
+#                     $indexes_set++;
+                                       $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = $remove_stopwords = 0;
+                }
+                # ISBN,ISSN,Standard Number, don't need special treatment
+                elsif ( $index eq 'nb' || $index eq 'ns' ) {
+#                     $indexes_set++;
+                    (   
                         $stemming,      $auto_truncation,
                         $weight_fields, $fuzzy_enabled,
                         $remove_stopwords
                     ) = ( 0, 0, 0, 0, 0 );
-                }
 
+                }
+                
+                if(not $index){
+                    $index = 'kw';
+                }
+                
                 # Set default structure attribute (word list)
                 my $struct_attr;
-                unless ( !$index || $index =~ /(st-|phr|ext|wrdl)/ ) {
+                unless ( $indexes_set || !$index || $index =~ /(st-|phr|ext|wrdl)/ ) {
                     $struct_attr = ",wrdl";
                 }
 
@@ -970,12 +1133,19 @@ 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 ( $nontruncated, $righttruncated, $lefttruncated,
-                    $rightlefttruncated, $regexpr );
                 my $truncated_operand;
-                (
-                    $nontruncated, $righttruncated, $lefttruncated,
+                my( $nontruncated, $righttruncated, $lefttruncated,
                     $rightlefttruncated, $regexpr
                 ) = _detect_truncation( $operand, $index );
                 warn
@@ -992,29 +1162,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;
                     }
                 }
@@ -1023,18 +1187,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) {
@@ -1108,7 +1273,17 @@ sub buildQuery {
             $limit .= " and " if $limit || $query;
             $limit      .= "$this_limit";
             $limit_cgi  .= "&limit=$this_limit";
-            $limit_desc .= " $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) {
@@ -1124,15 +1299,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;
@@ -1145,9 +1320,7 @@ sub buildQuery {
         warn "LIMIT:" . $limit;
         warn "LIMIT CGI:" . $limit_cgi;
         warn "LIMIT DESC:" . $limit_desc;
-        warn "---------";
-        warn "Leave buildQuery";
-        warn "---------";
+        warn "---------\nLeave buildQuery\n---------";
     }
     return (
         undef,              $query, $simple_query, $query_cgi,
@@ -1165,39 +1338,25 @@ Format results in a form suitable for passing to the template
 # IMO this subroutine is pretty messy still -- it's responsible for
 # building the HTML output for the template
 sub searchResults {
-    my ( $searchdesc, $hits, $results_per_page, $offset, @marcresults ) = @_;
+    my ( $searchdesc, $hits, $results_per_page, $offset, $scan, @marcresults ) = @_;
     my $dbh = C4::Context->dbh;
-    my $toggle;
-    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'};
     }
-    my %locations;
-    my $lsch =
-      $dbh->prepare(
-"SELECT authorised_value,lib FROM authorised_values WHERE category = 'LOC'"
-      );
-    $lsch->execute();
-    while ( my $ldata = $lsch->fetchrow_hashref ) {
-        $locations{ $ldata->{'authorised_value'} } = $ldata->{'lib'};
-    }
+# 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','');
+
+    # get notforloan authorised value list (see $shelflocations  FIXME)
+    my $notforloan_authorised_value = GetAuthValCode('items.notforloan','');
 
     #Build itemtype hash
     #find itemtype & itemtype image
@@ -1208,12 +1367,9 @@ sub searchResults {
       );
     $bsth->execute();
     while ( my $bdata = $bsth->fetchrow_hashref ) {
-        $itemtypes{ $bdata->{'itemtype'} }->{description} =
-          $bdata->{'description'};
-        $itemtypes{ $bdata->{'itemtype'} }->{imageurl} = $bdata->{'imageurl'};
-        $itemtypes{ $bdata->{'itemtype'} }->{summary}  = $bdata->{'summary'};
-        $itemtypes{ $bdata->{'itemtype'} }->{notforloan} =
-          $bdata->{'notforloan'};
+               foreach (qw(description imageurl summary notforloan)) {
+               $itemtypes{ $bdata->{'itemtype'} }->{$_} = $bdata->{$_};
+               }
     }
 
     #search item field code
@@ -1224,14 +1380,6 @@ sub searchResults {
     $sth->execute;
     my ($itemtag) = $sth->fetchrow;
 
-    # get notforloan authorised value list
-    $sth =
-      $dbh->prepare(
-"SELECT authorised_value FROM `marc_subfield_structure` WHERE kohafield = 'items.notforloan' AND frameworkcode=''"
-      );
-    $sth->execute;
-    my ($notforloan_authorised_value) = $sth->fetchrow;
-
     ## find column names of items related to MARC
     my $sth2 = $dbh->prepare("SHOW COLUMNS FROM items");
     $sth2->execute;
@@ -1248,119 +1396,97 @@ sub searchResults {
         $times = $offset + $results_per_page;
     }
     else {
-        $times = $hits;
+        $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;
-        $marcrecord = MARC::File::USMARC::decode( $marcresults[$i] );
-        my $oldbiblio = TransformMarcToKoha( $dbh, $marcrecord, '' );
+        my $marcrecord = MARC::File::USMARC::decode( $marcresults[$i] );
+           SetUTF8Flag($marcrecord);
+               my $biblionumber;
+
+        if(not $scan){
+            if ($bibliotag<10){
+                $biblionumber = $marcrecord->field($bibliotag) ? $marcrecord->field($bibliotag)->data : undef;
+            }else{
+                $biblionumber = $marcrecord->subfield($bibliotag,$bibliosubf);
+            } 
+            $fw = (defined $biblionumber) ? 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
-        if ( $itemtypes{ $oldbiblio->{itemtype} }->{imageurl} =~ /^http:/ ) {
-            $oldbiblio->{imageurl} =
-              $itemtypes{ $oldbiblio->{itemtype} }->{imageurl};
-            $oldbiblio->{description} =
-              $itemtypes{ $oldbiblio->{itemtype} }->{description};
-        }
-        else {
-            $oldbiblio->{imageurl} =
-              getitemtypeimagesrc() . "/"
-              . $itemtypes{ $oldbiblio->{itemtype} }->{imageurl}
-              if ( $itemtypes{ $oldbiblio->{itemtype} }->{imageurl} );
-            $oldbiblio->{description} =
-              $itemtypes{ $oldbiblio->{itemtype} }->{description};
-        }
-
+        $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'}, $marcrecord ) );
+        (my $aisbn) = $oldbiblio->{isbn} =~ /([\d-]*[X]*)/;
+        $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} ) {
             my $summary = $itemtypes{ $oldbiblio->{itemtype} }->{summary};
             my @fields  = $marcrecord->fields();
-            foreach my $field (@fields) {
-                my $tag      = $field->tag();
-                my $tagvalue = $field->as_string();
-                $summary =~
-                  s/\[(.?.?.?.?)$tag\*(.*?)]/$1$tagvalue$2\[$1$tag$2]/g;
-                unless ( $tag < 10 ) {
-                    my @subf = $field->subfields;
-                    for my $i ( 0 .. $#subf ) {
-                        my $subfieldcode  = $subf[$i][0];
-                        my $subfieldvalue = $subf[$i][1];
-                        my $tagsubf       = $tag . $subfieldcode;
-                        $summary =~
-s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
+            
+            my $newsummary;
+            foreach my $line ( "$summary\n" =~ /(.*)\n/g ){
+                my $tags = {};
+                foreach my $tag ( $line =~ /\[(\d{3}[\w|\d])\]/ ) {
+                    $tag =~ /(.{3})(.)/;
+                    if($marcrecord->field($1)){
+                        my @abc = $marcrecord->field($1)->subfield($2);
+                        $tags->{$tag} = $#abc + 1 ;
                     }
                 }
-            }
-            # FIXME: yuk
-            $summary =~ s/\[(.*?)]//g;
-            $summary =~ s/\n/<br>/g;
-            $oldbiblio->{summary} = $summary;
-        }
-
-# 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++;
+                
+                # We catch how many times to repeat this line
+                my $max = 0;
+                foreach my $tag (keys(%$tags)){
+                    $max = $tags->{$tag} if($tags->{$tag} > $max);
+                 }
+                
+                # we replace, and repeat each line
+                for (my $i = 0 ; $i < $max ; $i++){
+                    my $newline = $line;
+
+                    foreach my $tag ( $newline =~ /\[(\d{3}[\w|\d])\]/g ) {
+                        $tag =~ /(.{3})(.)/;
+                        
+                        if($marcrecord->field($1)){
+                            my @repl = $marcrecord->field($1)->subfield($2);
+                            my $subfieldvalue = $repl[$i];
+                            
+                            if (! utf8::is_utf8($subfieldvalue)) {
+                                utf8::decode($subfieldvalue);
+                            }
+                             $newline =~ s/\[$tag\]/$subfieldvalue/g;
                         }
                     }
-                    $searchhighlightblob .= $field . " ... " if $match;
+                    $newsummary .= "$newline\n";
                 }
-    
-            }
-            $searchhighlightblob = ' ... '.$searchhighlightblob if $searchhighlightblob;
-            $oldbiblio->{'searchhighlightblob'} = $searchhighlightblob;
-        }
-# save an author with no <span> tag, for the <a href=search.pl?q=<!--tmpl_var name="author"-->> link
-        $oldbiblio->{'author_nospan'} = $oldbiblio->{'author'};
-
-        # 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;
-                $oldbiblio->{'title'} =~
-                  s/$term/<span class=\"term\">$&<\/span>/gi;
-                $oldbiblio->{'subtitle'} =~
-                  s/$term/<span class=\"term\">$&<\/span>/gi;
-                $oldbiblio->{'author'} =~
-                  s/$term/<span class=\"term\">$&<\/span>/gi;
-                $oldbiblio->{'publishercode'} =~
-                  s/$term/<span class=\"term\">$&<\/span>/gi;
-                $oldbiblio->{'place'} =~
-                  s/$term/<span class=\"term\">$&<\/span>/gi;
-                $oldbiblio->{'pages'} =~
-                  s/$term/<span class=\"term\">$&<\/span>/gi;
-                $oldbiblio->{'notes'} =~
-                  s/$term/<span class=\"term\">$&<\/span>/gi;
-                $oldbiblio->{'size'} =~
-                  s/$term/<span class=\"term\">$&<\/span>/gi;
             }
-        }
 
-        # FIXME:
-        # surely there's a better way to handle this
-        if ( $i % 2 ) {
-            $toggle = "#ffffcc";
+            $newsummary =~ s/\[(.*?)]//g;
+            $newsummary =~ s/\n/<br\/>/g;
+            $oldbiblio->{summary} = $newsummary;
         }
-        else {
-            $toggle = "white";
-        }
-        $oldbiblio->{'toggle'} = $toggle;
 
         # Pull out the items fields
         my @fields = $marcrecord->field($itemtag);
@@ -1368,24 +1494,27 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
         # 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 $longoverdue_count = 0;
-        my $other_count       = 0;
-        my $wthdrawn_count    = 0;
-        my $itemlost_count    = 0;
-        my $itembinding_count = 0;
-        my $itemdamaged_count = 0;
-        my $can_place_holds   = 0;
-        my $items_count       = scalar(@fields);
-        my $items_counter;
+        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;
+        my $itemlost_count        = 0;
+        my $itembinding_count     = 0;
+        my $itemdamaged_count     = 0;
+        my $item_in_transit_count = 0;
+        my $can_place_holds       = 0;
+        my $items_count           = scalar(@fields);
         my $maxitems =
           ( C4::Context->preference('maxItemsinSearchResults') )
           ? C4::Context->preference('maxItemsinSearchResults') - 1
@@ -1394,39 +1523,41 @@ 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 ) {
                 $item->{$code} = $field->subfield( $subfieldstosearch{$code} );
             }
-
+                       my $hbranch     = C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ? 'homebranch'    : 'holdingbranch';
+                       my $otherbranch = C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ? 'holdingbranch' : 'homebranch';
             # set item's branch name, use HomeOrHoldingBranch syspref first, fall back to the other one
-            if ( $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} ) {
-                $item->{'branchname'} = $branches{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} };
+            if ($item->{$hbranch}) {
+                $item->{'branchname'} = $branches{$item->{$hbranch}};
             }
-            # Last resort
-            elsif ( $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'holdingbranch':'homebranch'} ) {
-                $item->{'branchname'} = $branches{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'holdingbranch':'homebranch'} };
+            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++;
-                $onloan_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{due_date} = format_date( $item->{onloan} );
-                $onloan_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{count}++ if $item->{'homebranch'};
-                $onloan_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{branchname} = $item->{'branchname'};
-                $onloan_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{location} = $locations{ $item->{location} };
-                $onloan_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{itemcallnumber} = $item->{itemcallnumber};
-        $onloan_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
+                               my $key = $prefix . $item->{onloan} . $item->{barcode};
+                               $onloan_items->{$key}->{due_date} = format_date($item->{onloan});
+                               $onloan_items->{$key}->{count}++ if $item->{$hbranch};
+                               $onloan_items->{$key}->{branchname} = $item->{branchname};
+                               $onloan_items->{$key}->{location} = $shelflocations->{ $item->{location} };
+                               $onloan_items->{$key}->{itemcallnumber} = $item->{itemcallnumber};
+                               $onloan_items->{$key}->{imageurl} = getitemtypeimagelocation( 'opac', $itemtypes{ $item->{itype} }->{imageurl} );
+                               $onloan_items->{$key}->{barcode} = $item->{barcode};
+                               $onloan_items->{$key}->{reserved} = $item->{reserved};
                 # if something's checked out and lost, mark it as 'long overdue'
                 if ( $item->{itemlost} ) {
-                    $onloan_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date} }->{longoverdue}++;
+                    $onloan_items->{$prefix}->{longoverdue}++;
                     $longoverdue_count++;
-                }
-
-                # can place holds as long as this item isn't lost
-                else {
+                } else {       # can place holds as long as item isn't lost
                     $can_place_holds = 1;
                 }
             }
@@ -1439,80 +1570,136 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
                     $ordered_count++;
                 }
 
+                # is item in transit?
+                my $transfertwhen = '';
+                my ($transfertfrom, $transfertto);
+                
+                unless ($item->{wthdrawn}
+                        || $item->{itemlost}
+                        || $item->{damaged}
+                        || $item->{notforloan}
+                        || $items_count > 20) {
+
+                    # A couple heuristics to limit how many times
+                    # we query the database for item transfer information, sacrificing
+                    # accuracy in some cases for speed;
+                    #
+                    # 1. don't query if item has one of the other statuses
+                    # 2. don't check transit status if the bib has
+                    #    more than 20 items
+                    #
+                    # FIXME: to avoid having the query the database like this, and to make
+                    #        the in transit status count as unavailable for search limiting,
+                    #        should map transit status to record indexed in Zebra.
+                    #
+                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
+                }
+
                 # item is withdrawn, lost or damaged
                 if (   $item->{wthdrawn}
                     || $item->{itemlost}
                     || $item->{damaged}
-                    || $item->{notforloan} )
+                    || $item->{notforloan} 
+                    || $item->{reserved}
+                    || ($transfertwhen ne ''))
                 {
-                    $wthdrawn_count++    if $item->{wthdrawn};
-                    $itemlost_count++    if $item->{itemlost};
-                    $itemdamaged_count++ if $item->{damaged};
+                    $wthdrawn_count++        if $item->{wthdrawn};
+                    $itemlost_count++        if $item->{itemlost};
+                    $itemdamaged_count++     if $item->{damaged};
+                    $item_in_transit_count++ if $transfertwhen ne '';
                     $item->{status} = $item->{wthdrawn} . "-" . $item->{itemlost} . "-" . $item->{damaged} . "-" . $item->{notforloan};
-                    $other_count++;
-
-                    $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{wthdrawn} = $item->{wthdrawn};
-                    $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{itemlost} = $item->{itemlost};
-                    $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{damaged} = $item->{damaged};
-                    $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{notforloan} = GetAuthorisedValueDesc( '', '', $item->{notforloan}, '', '', $notforloan_authorised_value ) if $notforloan_authorised_value;
-                    $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{count}++ if $item->{'homebranch'};
-                    $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{branchname} = $item->{'branchname'};
-                    $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{location} = $locations{ $item->{location} };
-                    $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{itemcallnumber} = $item->{itemcallnumber};
-            $other_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
-                }
 
+                                       my $key = $prefix . $item->{status};
+                                       
+                                       foreach (qw(wthdrawn itemlost damaged branchname itemcallnumber)) {
+                                           if($item->{notforloan} == 1){
+                                               $notforloan_items->{$key}->{$_} = $item->{$_};
+                                           }else{
+                          $other_items->{$key}->{$_} = $item->{$_};
+                                           }
+                                       }
+                                       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->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{count}++ if $item->{'homebranch'};
-                    $available_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{branchname} = $item->{'branchname'};
-                    $available_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{location} = $locations{ $item->{location} };
-                    $available_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{itemcallnumber} = $item->{itemcallnumber};
-            $available_items->{ $item->{C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch'?'homebranch':'holdingbranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
+                                       $available_items->{$prefix}->{count}++ if $item->{$hbranch};
+                                       foreach (qw(branchname itemcallnumber barcode)) {
+                       $available_items->{$prefix}->{$_} = $item->{$_};
+                                       }
+                                       $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} };
+                                       $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( 'opac', $itemtypes{ $item->{itype} }->{imageurl} );
                 }
             }
         }    # 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
           : 1;
         for my $key ( sort keys %$onloan_items ) {
-            $onloanitemscount++;
-            push @onloan_items_loop, $onloan_items->{$key}
-              unless $onloanitemscount > $maxitems;
+            (++$onloanitemscount > $maxitems) and last;
+            push @onloan_items_loop, $onloan_items->{$key};
         }
         for my $key ( sort keys %$other_items ) {
-            $otheritemscount++;
-            push @other_items_loop, $other_items->{$key}
-              unless $otheritemscount > $maxitems;
+            (++$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++;
+            (++$availableitemscount > $maxitems) and last;
             push @available_items_loop, $available_items->{$key}
-              unless $availableitemscount > $maxitems;
         }
 
-# 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};
+        # XSLT processing of some stuff
+        if (C4::Context->preference("XSLTResultsDisplay") && !$scan) {
+            $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display(
+                $oldbiblio->{biblionumber}, $marcrecord, C4::Context->preference("XSLTResultsDisplay") );
+        }
+
+        # 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};
         $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;
         $oldbiblio->{itemlostcount}        = $itemlost_count;
         $oldbiblio->{damagedcount}         = $itemdamaged_count;
+        $oldbiblio->{intransitcount}       = $item_in_transit_count;
         $oldbiblio->{orderedcount}         = $ordered_count;
         $oldbiblio->{isbn} =~
           s/-//g;    # deleting - in isbn to enable amazon content
@@ -1521,6 +1708,95 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
     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;
+}
+
 #----------------------------------------------------------------------
 #
 # Non-Zebra GetRecords#
@@ -1613,10 +1889,14 @@ sub NZanalyse {
         } 
     }
     warn "string :" . $string if $DEBUG;
-    $string =~ /(.*?)( and | or | not | AND | OR | NOT )(.*)/;
-    my $left     = $1;
-    my $right    = $3;
-    my $operator = lc($2);    # FIXME: and/or/not are operators, not operands
+    my $left = "";
+    my $right = "";
+    my $operator = "";
+    if ($string =~ /(.*?)( and | or | not | AND | OR | NOT )(.*)/) {
+        $left     = $1;
+        $right    = $3;
+        $operator = lc($2);    # FIXME: and/or/not are operators, not operands
+    }
     warn "no parenthesis. left : $left operator: $operator right: $right"
       if $DEBUG;
 
@@ -1657,28 +1937,39 @@ sub NZanalyse {
     else {
         $string =~ s/__X__/"$commacontent"/ if $commacontent;
         $string =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|&|\+|\*|\// /g;
+        #remove trailing blank at the beginning
+        $string =~ s/^ //g;
         warn "leaf:$string" if $DEBUG;
 
         # parse the string in in operator/operand/value again
-        $string =~ /(.*)(>=|<=)(.*)/;
-        my $left     = $1;
-        my $operator = $2;
-        my $right    = $3;
-#         warn "handling leaf... left:$left operator:$operator right:$right"
-#           if $DEBUG;
-        unless ($operator) {
-            $string =~ /(.*)(>|<|=)(.*)/;
+        my $left = "";
+        my $operator = "";
+        my $right = "";
+        if ($string =~ /(.*)(>=|<=)(.*)/) {
             $left     = $1;
             $operator = $2;
             $right    = $3;
-#             warn
-# "handling unless (operator)... left:$left operator:$operator right:$right"
-#               if $DEBUG;
+        } else {
+            $left = $string;
+        }
+#         warn "handling leaf... left:$left operator:$operator right:$right"
+#           if $DEBUG;
+        unless ($operator) {
+            if ($string =~ /(.*)(>|<|=)(.*)/) {
+                $left     = $1;
+                $operator = $2;
+                $right    = $3;
+                warn
+    "handling unless (operator)... left:$left operator:$operator right:$right"
+                if $DEBUG;
+            } else {
+                $left = $string;
+            }
         }
         my $results;
 
 # strip adv, zebra keywords, currently not handled in nozebra: wrdl, ext, phr...
-        $left =~ s/[, ].*$//;
+        $left =~ s/ .*$//;
 
         # automatic replace for short operators
         $left = 'title'            if $left =~ '^ti$';
@@ -1687,17 +1978,16 @@ sub NZanalyse {
         $left = 'subject'          if $left =~ '^su$';
         $left = 'koha-Auth-Number' if $left =~ '^an$';
         $left = 'keyword'          if $left =~ '^kw$';
-        warn "handling leaf... left:$left operator:$operator right:$right";
+        $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";
+            );
+            warn "$left / $operator / $right\n" if $DEBUG;
 
             # split each word, query the DB and build the biblionumbers result
             #sanitizing leftpart
@@ -1706,7 +1996,7 @@ sub NZanalyse {
                 my $biblionumbers;
                 $_ =~ s/^\s+|\s+$//;
                 next unless $_;
-                warn "EXECUTE : $server, $left, $_";
+                warn "EXECUTE : $server, $left, $_" if $DEBUG;
                 $sth->execute( $server, $left, $_ )
                   or warn "execute failed: $!";
                 while ( my ( $line, $value ) = $sth->fetchrow ) {
@@ -1717,30 +2007,26 @@ sub NZanalyse {
                       unless ( $right =~ /^\d+$/ && $value =~ /\D/ );
                     warn "result : $value "
                       . ( $right  =~ /\d/ ) . "=="
-                      . ( $value =~ /\D/?$line:"" );         #= $line";
+                      . ( $value =~ /\D/?$line:"" ) if $DEBUG;         #= $line";
                 }
 
 # do a AND with existing list if there is one, otherwise, use the biblionumbers list as 1st result list
                 if ($results) {
-                    warn "NZAND";        
+                    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 ) {
+            foreach ( split (/ /, $string )) {
                 next if C4::Context->stopwords->{ uc($_) };   # skip if stopword
                 warn "search on all indexes on $_" if $DEBUG;
                 my $biblionumbers;
@@ -1763,15 +2049,13 @@ sub NZanalyse {
         warn "return : $results for LEAF : $string" if $DEBUG;
         return $results;
     }
-    warn "---------"       if $DEBUG;
-    warn "Leave NZanalyse" if $DEBUG;
-    warn "---------"       if $DEBUG;
+    warn "---------\nLeave NZanalyse\n---------" if $DEBUG;
 }
 
 sub NZoperatorAND{
     my ($rightresult, $leftresult)=@_;
     
-    my @leftresult = split /;/, $leftresult;
+    my @leftresult = split (/;/, $leftresult);
     warn " @leftresult / $rightresult \n" if $DEBUG;
     
     #             my @rightresult = split /;/,$leftresult;
@@ -1785,13 +2069,13 @@ sub NZoperatorAND{
         my $value = $_;
         my $countvalue;
         ( $value, $countvalue ) = ( $1, $2 ) if ($value=~/(.*)-(\d+)$/);
-        if ( $rightresult =~ /$value-(\d+);/ ) {
+        if ( $rightresult =~ /\Q$value\E-(\d+);/ ) {
             $countvalue = ( $1 > $countvalue ? $countvalue : $1 );
             $finalresult .=
                 "$value-$countvalue;$value-$countvalue;";
         }
     }
-    warn " $finalresult \n" if $DEBUG;
+    warn "NZAND DONE : $finalresult \n" if $DEBUG;
     return $finalresult;
 }
       
@@ -1801,7 +2085,7 @@ sub NZoperatorOR{
 }
 
 sub NZoperatorNOT{
-    my ($rightresult, $leftresult)=@_;
+    my ($leftresult, $rightresult)=@_;
     
     my @leftresult = split /;/, $leftresult;
 
@@ -1845,14 +2129,14 @@ sub NZorder {
         # popularity is not in MARC record, it's builded from a specific query
         my $sth =
           $dbh->prepare("select sum(issues) from items where biblionumber=?");
-        foreach ( split /;/, $biblionumbers ) {
-            my ( $biblionumber, $title ) = split /,/, $_;
+        foreach ( split (/;/, $biblionumbers )) {
+            my ( $biblionumber, $title ) = split (/,/, $_);
             $result{$biblionumber} = GetMarcBiblio($biblionumber);
             $sth->execute($biblionumber);
             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
@@ -1885,8 +2169,8 @@ sub NZorder {
     }
     elsif ( $ordering =~ /author/ ) {
         my %result;
-        foreach ( split /;/, $biblionumbers ) {
-            my ( $biblionumber, $title ) = split /,/, $_;
+        foreach ( split (/;/, $biblionumbers )) {
+            my ( $biblionumber, $title ) = split (/,/, $_);
             my $record = GetMarcBiblio($biblionumber);
             my $author;
             if ( C4::Context->preference('marcflavour') eq 'UNIMARC' ) {
@@ -1928,14 +2212,14 @@ sub NZorder {
     }
     elsif ( $ordering =~ /callnumber/ ) {
         my %result;
-        foreach ( split /;/, $biblionumbers ) {
-            my ( $biblionumber, $title ) = split /,/, $_;
+        foreach ( split (/;/, $biblionumbers )) {
+            my ( $biblionumber, $title ) = split (/,/, $_);
             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' );
@@ -1971,8 +2255,8 @@ sub NZorder {
     }
     elsif ( $ordering =~ /pubdate/ ) {             #pub year
         my %result;
-        foreach ( split /;/, $biblionumbers ) {
-            my ( $biblionumber, $title ) = split /,/, $_;
+        foreach ( split (/;/, $biblionumbers )) {
+            my ( $biblionumber, $title ) = split (/,/, $_);
             my $record = GetMarcBiblio($biblionumber);
             my ( $publicationyear_tag, $publicationyear_subfield ) =
               GetMarcFromKohaField( 'biblioitems.publicationyear', '' );
@@ -2013,8 +2297,8 @@ sub NZorder {
 
 # the title is in the biblionumbers string, so we just need to build a hash, sort it and return
         my %result;
-        foreach ( split /;/, $biblionumbers ) {
-            my ( $biblionumber, $title ) = split /,/, $_;
+        foreach ( split (/;/, $biblionumbers )) {
+            my ( $biblionumber, $title ) = split (/,/, $_);
 
 # hint : the result is sorted by title.biblionumber because we can have X biblios with the same title
 # and we don't want to get only 1 result for each of them !!!
@@ -2113,130 +2397,127 @@ 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<input arg:>
+=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 enabled_opac_search_views
+
+%hash = enabled_opac_search_views()
 
-    * $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.
+This function returns a hash that contains two flags obtained from the system
+preferences, used to determine whether a particular opac search results view
+is enabled.
+
+=over 2
 
 =item C<Output arg:>
-    * $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
 
 =item C<usage in the script:>
 
 =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_opac_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_opac_search_views
+{
+       return (
+               can_opac_view_MARC              => C4::Context->preference('OPACviewMARC'),             # 1 if the opac search allows the MARC view
+               can_opac_view_ISBD              => C4::Context->preference('OPACviewISBD'),             # 1 if the opac search allows the ISBD 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<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 ( $countmatched, \@unmatched );
+    return $array;
 }
 
+
 END { }    # module clean-up code here (global destructor)
 
 1;