Bug 14695 [QA Followup] - Disable "Holds to place (count)" unless "Hold next availabl...
[koha.git] / C4 / Biblio.pm
index 06d0ac1..3a34d79 100644 (file)
@@ -37,12 +37,15 @@ use C4::ClassSource;
 use C4::Charset;
 use C4::Linker;
 use C4::OAI::Sets;
+use C4::Debug;
 
 use Koha::Cache;
 use Koha::Authority::Types;
 use Koha::Acquisition::Currencies;
+use Koha::SearchEngine;
 
 use vars qw(@ISA @EXPORT);
+use vars qw($debug $cgi_debug);
 
 BEGIN {
 
@@ -1206,18 +1209,44 @@ C<$frameworkcode> is the framework code.
 
 sub GetUsedMarcStructure {
     my $frameworkcode = shift || '';
-    my $query = qq/
+    my $query = q{
         SELECT *
         FROM   marc_subfield_structure
         WHERE   tab > -1 
             AND frameworkcode = ?
         ORDER BY tagfield, tagsubfield
-    /;
+    };
     my $sth = C4::Context->dbh->prepare($query);
     $sth->execute($frameworkcode);
     return $sth->fetchall_arrayref( {} );
 }
 
+=head2 GetMarcSubfieldStructure
+
+=cut
+
+sub GetMarcSubfieldStructure {
+    my ( $frameworkcode ) = @_;
+
+    $frameworkcode //= '';
+
+    my $cache     = Koha::Cache->get_instance();
+    my $cache_key = "MarcSubfieldStructure-$frameworkcode";
+    my $cached    = $cache->get_from_cache($cache_key);
+    return $cached if $cached;
+
+    my $dbh = C4::Context->dbh;
+    my $subfield_structure = $dbh->selectall_hashref( q|
+        SELECT *
+        FROM marc_subfield_structure
+        WHERE frameworkcode = ?
+        AND kohafield > ''
+    |, 'kohafield', {}, $frameworkcode );
+
+    $cache->set_in_cache( $cache_key, $subfield_structure );
+    return $subfield_structure;
+}
+
 =head2 GetMarcFromKohaField
 
   ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
@@ -1228,14 +1257,10 @@ for the given frameworkcode or default framework if $frameworkcode is missing
 =cut
 
 sub GetMarcFromKohaField {
-    my $kohafield = shift;
-    my $frameworkcode = shift || '';
+    my ( $kohafield, $frameworkcode ) = @_;
     return (0, undef) unless $kohafield;
-    my $relations = C4::Context->marcfromkohafield;
-    if ( my $mf = $relations->{$frameworkcode}->{$kohafield} ) {
-        return @$mf;
-    }
-    return (0, undef);
+    my $mss = GetMarcSubfieldStructure( $frameworkcode );
+    return ( $mss->{$kohafield}{tagfield}, $mss->{$kohafield}{tagsubfield} );
 }
 
 =head2 GetMarcSubfieldStructureFromKohaField
@@ -1250,24 +1275,14 @@ $frameworkcode is optional. If not given, then the default framework is used.
 =cut
 
 sub GetMarcSubfieldStructureFromKohaField {
-    my ($kohafield, $frameworkcode) = @_;
-
-    return undef unless $kohafield;
-    $frameworkcode //= '';
+    my ( $kohafield, $frameworkcode ) = @_;
 
-    my $dbh = C4::Context->dbh;
-    my $query = qq{
-        SELECT *
-        FROM marc_subfield_structure
-        WHERE kohafield = ?
-          AND frameworkcode = ?
-    };
-    my $sth = $dbh->prepare($query);
-    $sth->execute($kohafield, $frameworkcode);
-    my $result = $sth->fetchrow_hashref;
-    $sth->finish;
+    return unless $kohafield;
 
-    return $result;
+    my $mss = GetMarcSubfieldStructure( $frameworkcode );
+    return exists $mss->{$kohafield}
+        ? $mss->{$kohafield}
+        : undef;
 }
 
 =head2 GetMarcBiblio
@@ -2228,17 +2243,18 @@ sub TransformKohaToMarc {
     my $hash = shift;
     my $record = MARC::Record->new();
     SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
-    my $db_to_marc = C4::Context->marcfromkohafield;
+    # FIXME Do not we want to get the marc subfield structure for the biblio framework?
+    my $mss = GetMarcSubfieldStructure();
     my $tag_hr = {};
-    while ( my ($name, $value) = each %$hash ) {
-        next unless my $dtm = $db_to_marc->{''}->{$name};
-        next unless ( scalar( @$dtm ) );
-        my ($tag, $letter) = @$dtm;
-        $tag .= '';
+    while ( my ($kohafield, $value) = each %$hash ) {
+        next unless exists $mss->{$kohafield};
+        next unless $mss->{$kohafield};
+        my $tagfield    = $mss->{$kohafield}{tagfield} . '';
+        my $tagsubfield = $mss->{$kohafield}{tagsubfield};
         foreach my $value ( split(/\s?\|\s?/, $value, -1) ) {
             next if $value eq '';
-            $tag_hr->{$tag} //= [];
-            push @{$tag_hr->{$tag}}, [($letter, $value)];
+            $tag_hr->{$tagfield} //= [];
+            push @{$tag_hr->{$tagfield}}, [($tagsubfield, $value)];
         }
     }
     foreach my $tag (sort keys %$tag_hr) {
@@ -2513,7 +2529,7 @@ sub _default_ind_to_space {
 sub TransformHtmlToMarc {
     my ($cgi, $isbiblio) = @_;
 
-    my @params = $cgi->param();
+    my @params = $cgi->multi_param();
 
     # explicitly turn on the UTF-8 flag for all
     # 'tag_' parameters to avoid incorrect character
@@ -2542,9 +2558,9 @@ sub TransformHtmlToMarc {
         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
         if ( $param eq 'biblionumber' ) {
             if ( $biblionumbertagfield < 10 ) {
-                $newfield = MARC::Field->new( $biblionumbertagfield, $cgi->param($param), );
+                $newfield = MARC::Field->new( $biblionumbertagfield, scalar $cgi->param($param), );
             } else {
-                $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => $cgi->param($param), );
+                $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => scalar $cgi->param($param), );
             }
             push @fields, $newfield if ($newfield);
         } elsif ( $param =~ /^tag_(\d*)_indicator1_/ ) {    # new field start when having 'input name="..._indicator1_..."
@@ -2558,18 +2574,19 @@ sub TransformHtmlToMarc {
             if ( $tag < 10 ) {                              # no code for theses fields
                                                             # in MARC editor, 000 contains the leader.
                 next if $tag == $biblionumbertagfield;
+                my $fval= $cgi->param($params[$j+1]);
                 if ( $tag eq '000' ) {
                     # Force a fake leader even if not provided to avoid crashing
                     # during decoding MARC record containing UTF-8 characters
                     $record->leader(
-                        length( $cgi->param($params[$j+1]) ) == 24
-                        ? $cgi->param( $params[ $j + 1 ] )
+                        length( $fval ) == 24
+                        ? $fval
                         : '     nam a22        4500'
                        )
                     ;
                     # between 001 and 009 (included)
-                } elsif ( $cgi->param( $params[ $j + 1 ] ) ne '' ) {
-                    $newfield = MARC::Field->new( $tag, $cgi->param( $params[ $j + 1 ] ), );
+                } elsif ( $fval ne '' ) {
+                    $newfield = MARC::Field->new( $tag, $fval, );
                 }
 
                 # > 009, deal with subfields
@@ -2583,13 +2600,14 @@ sub TransformHtmlToMarc {
                     #if next param ne subfield, then it was probably empty
                     #try next param by incrementing j
                     if($params[$j+1]!~/_subfield_/) {$j++; next; }
+                    my $fkey= $cgi->param($params[$j]);
                     my $fval= $cgi->param($params[$j+1]);
                     #check if subfield value not empty and field exists
                     if($fval ne '' && $newfield) {
-                        $newfield->add_subfields( $cgi->param($params[$j]) => $fval);
+                        $newfield->add_subfields( $fkey => $fval);
                     }
                     elsif($fval ne '') {
-                        $newfield = MARC::Field->new( $tag, $ind1, $ind2, $cgi->param($params[$j]) => $fval );
+                        $newfield = MARC::Field->new( $tag, $ind1, $ind2, $fkey => $fval );
                     }
                     $j += 2;
                 } #end-of-while
@@ -2603,9 +2621,6 @@ sub TransformHtmlToMarc {
     return $record;
 }
 
-# cache inverted MARC field map
-our $inverted_field_map;
-
 =head2 TransformMarcToKoha
 
   $result = TransformMarcToKoha( $record, $frameworkcode )
@@ -2629,9 +2644,7 @@ sub TransformMarcToKoha {
     $limit_table = $limit_table || 0;
     $frameworkcode = '' unless defined $frameworkcode;
 
-    unless ( defined $inverted_field_map ) {
-        $inverted_field_map = _get_inverted_marc_field_map();
-    }
+    my $inverted_field_map = _get_inverted_marc_field_map($frameworkcode);
 
     my %tables = ();
     if ( defined $limit_table && $limit_table eq 'items' ) {
@@ -2645,9 +2658,9 @@ sub TransformMarcToKoha {
     # traverse through record
   MARCFIELD: foreach my $field ( $record->fields() ) {
         my $tag = $field->tag();
-        next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
+        next MARCFIELD unless exists $inverted_field_map->{$tag};
         if ( $field->is_control_field() ) {
-            my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
+            my $kohafields = $inverted_field_map->{$tag}->{list};
           ENTRY: foreach my $entry ( @{$kohafields} ) {
                 my ( $subfield, $table, $column ) = @{$entry};
                 next ENTRY unless exists $tables{$table};
@@ -2665,9 +2678,9 @@ sub TransformMarcToKoha {
             # deal with subfields
           MARCSUBFIELD: foreach my $sf ( $field->subfields() ) {
                 my $code = $sf->[0];
-                next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
+                next MARCSUBFIELD unless exists $inverted_field_map->{$tag}->{sfs}->{$code};
                 my $value = $sf->[1];
-              SFENTRY: foreach my $entry ( @{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} } ) {
+              SFENTRY: foreach my $entry ( @{ $inverted_field_map->{$tag}->{sfs}->{$code} } ) {
                     my ( $table, $column ) = @{$entry};
                     next SFENTRY unless exists $tables{$table};
                     my $key = _disambiguate( $table, $column );
@@ -2710,18 +2723,17 @@ sub TransformMarcToKoha {
 }
 
 sub _get_inverted_marc_field_map {
+    my ( $frameworkcode ) = @_;
     my $field_map = {};
-    my $relations = C4::Context->marcfromkohafield;
+    my $mss = GetMarcSubfieldStructure( $frameworkcode );
 
-    foreach my $frameworkcode ( keys %{$relations} ) {
-        foreach my $kohafield ( keys %{ $relations->{$frameworkcode} } ) {
-            next unless @{ $relations->{$frameworkcode}->{$kohafield} };    # not all columns are mapped to MARC tag & subfield
-            my $tag      = $relations->{$frameworkcode}->{$kohafield}->[0];
-            my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
-            my ( $table, $column ) = split /[.]/, $kohafield, 2;
-            push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
-            push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
-        }
+    foreach my $kohafield ( keys %{ $mss } ) {
+        next unless exists $mss->{$kohafield};    # not all columns are mapped to MARC tag & subfield
+        my $tag      = $mss->{$kohafield}{tagfield};
+        my $subfield = $mss->{$kohafield}{tagsubfield};
+        my ( $table, $column ) = split /[.]/, $kohafield, 2;
+        push @{ $field_map->{$tag}->{list} }, [ $subfield, $table, $column ];
+        push @{ $field_map->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
     }
     return $field_map;
 }
@@ -2888,31 +2900,60 @@ sub TransformMarcToKohaOneField {
 
 =head2 ModZebra
 
-  ModZebra( $biblionumber, $op, $server );
+  ModZebra( $biblionumber, $op, $server, $record );
 
 $biblionumber is the biblionumber we want to index
 
-$op is specialUpdate or delete, and is used to know what we want to do
+$op is specialUpdate or recordDelete, and is used to know what we want to do
 
 $server is the server that we want to update
 
+$record is the update MARC record if it's available. If it's not supplied
+and is needed, it'll be loaded from the database.
+
 =cut
 
 sub ModZebra {
 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
-    my ( $biblionumber, $op, $server ) = @_;
+    my ( $biblionumber, $op, $server, $record ) = @_;
+    $debug && warn "ModZebra: update requested for: $biblionumber $op $server\n";
+    if ( C4::Context->preference('SearchEngine') eq 'Elasticsearch' ) {
+
+        # TODO abstract to a standard API that'll work for whatever
+        require Koha::ElasticSearch::Indexer;
+        my $indexer = Koha::ElasticSearch::Indexer->new(
+            {
+                index => $server eq 'biblioserver'
+                ? $Koha::SearchEngine::BIBLIOS_INDEX
+                : $Koha::SearchEngine::AUTHORITIES_INDEX
+            }
+        );
+        if ( $op eq 'specialUpdate' ) {
+            unless ($record) {
+                $record = GetMarcBiblio($biblionumber, 1);
+            }
+            my $records = [$record];
+            $indexer->update_index_background( [$biblionumber], [$record] );
+        }
+        elsif ( $op eq 'recordDelete' ) {
+            $indexer->delete_index_background( [$biblionumber] );
+        }
+        else {
+            croak "ModZebra called with unknown operation: $op";
+        }
+    }
+
     my $dbh = C4::Context->dbh;
 
     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
     # at the same time
     # replaced by a zebraqueue table, that is filled with ModZebra to run.
     # the table is emptied by rebuild_zebra.pl script (using the -z switch)
-
     my $check_sql = "SELECT COUNT(*) FROM zebraqueue
-                     WHERE server = ?
-                     AND   biblio_auth_number = ?
-                     AND   operation = ?
-                     AND   done = 0";
+    WHERE server = ?
+        AND   biblio_auth_number = ?
+        AND   operation = ?
+        AND   done = 0";
     my $check_sth = $dbh->prepare_cached($check_sql);
     $check_sth->execute( $server, $biblionumber, $op );
     my ($count) = $check_sth->fetchrow_array;
@@ -3468,80 +3509,10 @@ sub ModBiblioMarc {
     $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding), $biblionumber );
     $sth->finish;
-    if ( C4::Context->preference('SearchEngine') eq 'ElasticSearch' ) {
-# shift to its on sub, so it can do it realtime or queue
-        can_load( modules => { 'Koha::ElasticSearch::Indexer' => undef } );
-        # need to get this from syspref probably biblio/authority for index
-        my $indexer = Koha::ElasticSearch::Indexer->new();
-        my $records = [$record];
-        $indexer->update_index([$biblionumber], $records);
-    }
-    ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
+    ModZebra( $biblionumber, "specialUpdate", "biblioserver", $record );
     return $biblionumber;
 }
 
-=head2 get_biblio_authorised_values
-
-find the types and values for all authorised values assigned to this biblio.
-
-parameters:
-    biblionumber
-    MARC::Record of the bib
-
-returns: a hashref mapping the authorised value to the value set for this biblionumber
-
-  $authorised_values = {
-                       'Scent'     => 'flowery',
-                       'Audience'  => 'Young Adult',
-                       'itemtypes' => 'SER',
-                        };
-
-Notes: forlibrarian should probably be passed in, and called something different.
-
-=cut
-
-sub get_biblio_authorised_values {
-    my $biblionumber = shift;
-    my $record       = shift;
-
-    my $forlibrarian  = 1;                                 # are we in staff or opac?
-    my $frameworkcode = GetFrameworkCode($biblionumber);
-
-    my $authorised_values;
-
-    my $tagslib = GetMarcStructure( $forlibrarian, $frameworkcode )
-      or return $authorised_values;
-
-    # assume that these entries in the authorised_value table are bibliolevel.
-    # ones that start with 'item%' are item level.
-    my $query = q(SELECT distinct authorised_value, kohafield
-                    FROM marc_subfield_structure
-                    WHERE authorised_value !=''
-                      AND (kohafield like 'biblio%'
-                       OR  kohafield like '') );
-    my $bibliolevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
-
-    foreach my $tag ( keys(%$tagslib) ) {
-        foreach my $subfield ( keys( %{ $tagslib->{$tag} } ) ) {
-
-            # warn "checking $subfield. type is: " . ref $tagslib->{ $tag }{ $subfield };
-            if ( 'HASH' eq ref $tagslib->{$tag}{$subfield} ) {
-                if ( defined $tagslib->{$tag}{$subfield}{'authorised_value'} && exists $bibliolevel_authorised_values->{ $tagslib->{$tag}{$subfield}{'authorised_value'} } ) {
-                    if ( defined $record->field($tag) ) {
-                        my $this_subfield_value = $record->field($tag)->subfield($subfield);
-                        if ( defined $this_subfield_value ) {
-                            $authorised_values->{ $tagslib->{$tag}{$subfield}{'authorised_value'} } = $this_subfield_value;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    # warn ( Data::Dumper->Dump( [ $authorised_values ], [ 'authorised_values' ] ) );
-    return $authorised_values;
-}
-
 =head2 CountBiblioInOrders
 
     $count = &CountBiblioInOrders( $biblionumber);