Bug 6716 - Statistics Table Documentation
[koha_gimpoz] / C4 / Biblio.pm
old mode 100755 (executable)
new mode 100644 (file)
index 120592f..96baaef
@@ -2,6 +2,7 @@ package C4::Biblio;
 
 # Copyright 2000-2002 Katipo Communications
 # Copyright 2010 BibLibre
+# Copyright 2011 Equinox Software, Inc.
 #
 # This file is part of Koha.
 #
@@ -20,6 +21,7 @@ package C4::Biblio;
 
 use strict;
 use warnings;
+use Carp;
 
 # use utf8;
 use MARC::Record;
@@ -35,6 +37,7 @@ use C4::ClassSource;
 use C4::Charset;
 require C4::Heading;
 require C4::Serials;
+require C4::Items;
 
 use vars qw($VERSION @ISA @EXPORT);
 
@@ -292,58 +295,30 @@ and biblionumber data for indexing.
 
 sub ModBiblio {
     my ( $record, $biblionumber, $frameworkcode ) = @_;
+    croak "No record" unless $record;
+
     if ( C4::Context->preference("CataloguingLog") ) {
         my $newrecord = GetMarcBiblio($biblionumber);
         logaction( "CATALOGUING", "MODIFY", $biblionumber, "BEFORE=>" . $newrecord->as_formatted );
     }
 
-    SetUTF8Flag($record);
-    my $dbh = C4::Context->dbh;
-
-    $frameworkcode = "" unless $frameworkcode;
-
-    # get the items before and append them to the biblio before updating the record, atm we just have the biblio
-    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
-    my $oldRecord = GetMarcBiblio($biblionumber);
-
-    # delete any item fields from incoming record to avoid
-    # duplication or incorrect data - use AddItem() or ModItem()
-    # to change items
-    foreach my $field ( $record->field($itemtag) ) {
-        $record->delete_field($field);
-    }
-
-    # parse each item, and, for an unknown reason, re-encode each subfield
-    # if you don't do that, the record will have encoding mixed
-    # and the biblio will be re-encoded.
-    # strange, I (Paul P.) searched more than 1 day to understand what happends
-    # but could only solve the problem this way...
-    my @fields = $oldRecord->field($itemtag);
-    foreach my $fielditem (@fields) {
-        my $field;
-        foreach ( $fielditem->subfields() ) {
-            # re-encode the subfield only if it isn't already in utf-8.
-            my ($tag, $value) = @$_;
-            $tag = Encode::encode('utf-8', $tag) unless utf8::is_utf8($tag);
-            $value = Encode::encode('utf-8', $value) unless utf8::is_utf8($value);
-
-            if ($field) {
-                $field->add_subfields( $tag => $value );
-            } else {
-                $field = MARC::Field->new( "$itemtag", '', '', $tag => $value );
-            }
-        }
-        $record->append_fields($field);
-    }
-
+    # Cleaning up invalid fields must be done early or SetUTF8Flag is liable to
+    # throw an exception which probably won't be handled.
     foreach my $field ($record->fields()) {
         if (! $field->is_control_field()) {
-            if (scalar($field->subfields()) == 0) {
-                $record->delete_fields($field);
+            if (scalar($field->subfields()) == 0 || (scalar($field->subfields()) == 1 && $field->subfield('9'))) {
+                $record->delete_field($field);
             }
         }
     }
 
+    SetUTF8Flag($record);
+    my $dbh = C4::Context->dbh;
+
+    $frameworkcode = "" unless $frameworkcode;
+
+    _strip_item_fields($record, $frameworkcode);
+
     # update biblionumber and biblioitemnumber in MARC
     # FIXME - this is assuming a 1 to 1 relationship between
     # biblios and biblioitems
@@ -368,6 +343,29 @@ sub ModBiblio {
     return 1;
 }
 
+=head2 _strip_item_fields
+
+  _strip_item_fields($record, $frameworkcode)
+
+Utility routine to remove item tags from a
+MARC bib.
+
+=cut
+
+sub _strip_item_fields {
+    my $record = shift;
+    my $frameworkcode = shift;
+    # get the items before and append them to the biblio before updating the record, atm we just have the biblio
+    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
+
+    # delete any item fields from incoming record to avoid
+    # duplication or incorrect data - use AddItem() or ModItem()
+    # to change items
+    foreach my $field ( $record->field($itemtag) ) {
+        $record->delete_field($field);
+    }
+}
+
 =head2 ModBiblioframework
 
    ModBiblioframework($biblionumber,$frameworkcode);
@@ -760,7 +758,7 @@ Return the ISBD view which can be included in opac and intranet
 
 sub GetISBDView {
     my ( $biblionumber, $template ) = @_;
-    my $record   = GetMarcBiblio($biblionumber);
+    my $record   = GetMarcBiblio($biblionumber, 1);
     return undef unless defined $record;
     my $itemtype = &GetFrameworkCode($biblionumber);
     my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch", $itemtype );
@@ -1047,16 +1045,18 @@ sub GetMarcFromKohaField {
 
 =head2 GetMarcBiblio
 
-  my $record = GetMarcBiblio($biblionumber);
+  my $record = GetMarcBiblio($biblionumber, [$embeditems]);
 
 Returns MARC::Record representing bib identified by
 C<$biblionumber>.  If no bib exists, returns undef.
-The MARC record contains both biblio & item data.
+C<$embeditems>.  If set to true, items data are included.
+The MARC record contains biblio data, and items data if $embeditems is set to true.
 
 =cut
 
 sub GetMarcBiblio {
     my $biblionumber = shift;
+    my $embeditems   = shift || 0;
     my $dbh          = C4::Context->dbh;
     my $sth          = $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
     $sth->execute($biblionumber);
@@ -1068,6 +1068,9 @@ sub GetMarcBiblio {
     if ($marcxml) {
         $record = eval { MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour') ) };
         if ($@) { warn " problem with :$biblionumber : $@ \n$marcxml"; }
+        return unless $record;
+
+       C4::Biblio::EmbedItemsInMarcBiblio($record, $biblionumber) if ($embeditems);
 
         #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
         return $record;
@@ -1177,8 +1180,8 @@ sub GetCOinSBiblio {
     if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
 
         # Setting datas
-        $aulast  = $record->subfield( '700', 'a' );
-        $aufirst = $record->subfield( '700', 'b' );
+        $aulast  = $record->subfield( '700', 'a' ) || '';
+        $aufirst = $record->subfield( '700', 'b' ) || '';
         $oauthors = "&amp;rft.au=$aufirst $aulast";
 
         # others authors
@@ -1191,10 +1194,10 @@ sub GetCOinSBiblio {
           ( $mtx eq 'dc' )
           ? "&amp;rft.title=" . $record->subfield( '200', 'a' )
           : "&amp;rft.title=" . $record->subfield( '200', 'a' ) . "&amp;rft.btitle=" . $record->subfield( '200', 'a' );
-        $pubyear   = $record->subfield( '210', 'd' );
-        $publisher = $record->subfield( '210', 'c' );
-        $isbn      = $record->subfield( '010', 'a' );
-        $issn      = $record->subfield( '011', 'a' );
+        $pubyear   = $record->subfield( '210', 'd' ) || '';
+        $publisher = $record->subfield( '210', 'c' ) || '';
+        $isbn      = $record->subfield( '010', 'a' ) || '';
+        $issn      = $record->subfield( '011', 'a' ) || '';
     } else {
 
         # MARC21 need some improve
@@ -1510,7 +1513,10 @@ sub GetMarcSubjects {
             my $value     = $subject_subfield->[1];
             my $linkvalue = $value;
             $linkvalue =~ s/(\(|\))//g;
-            my $operator = " and " unless $counter == 0;
+            my $operator;
+            if ( $counter != 0 ) {
+                $operator = ' and ';
+            }
             if ( $code eq 9 ) {
                 $found9 = 1;
                 @link_loop = ( { 'limit' => 'an', link => "$linkvalue" } );
@@ -1518,7 +1524,10 @@ sub GetMarcSubjects {
             if ( not $found9 ) {
                 push @link_loop, { 'limit' => $subject_limit, link => $linkvalue, operator => $operator };
             }
-            my $separator = C4::Context->preference("authoritysep") unless $counter == 0;
+            my $separator;
+            if ( $counter != 0 ) {
+                $separator = C4::Context->preference('authoritysep');
+            }
 
             # ignore $9
             my @this_link_loop = @link_loop;
@@ -1576,7 +1585,10 @@ sub GetMarcAuthors {
             my $value        = $authors_subfield->[1];
             my $linkvalue    = $value;
             $linkvalue =~ s/(\(|\))//g;
-            my $operator = " and " unless $count_auth == 0;
+            my $operator;
+            if ( $count_auth != 0 ) {
+                $operator = ' and ';
+            }
 
             # if we have an authority link, use that as the link, otherwise use standard searching
             if ($subfield9) {
@@ -1592,8 +1604,17 @@ sub GetMarcAuthors {
             $value = GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib )
               if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /4/ ) );
             my @this_link_loop = @link_loop;
-            my $separator = C4::Context->preference("authoritysep") unless $count_auth == 0;
-            push @subfields_loop, { code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator } unless ( $authors_subfield->[0] eq '9' );
+            my $separator;
+            if ( $count_auth != 0 ) {
+                $separator = C4::Context->preference('authoritysep');
+            }
+            push @subfields_loop,
+              { code      => $subfieldcode,
+                value     => $value,
+                link_loop => \@this_link_loop,
+                separator => $separator
+              }
+              unless ( $authors_subfield->[0] eq '9' );
             $count_auth++;
         }
         push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
@@ -1704,13 +1725,27 @@ sub GetMarcSeries {
             my $value     = $series_subfield->[1];
             my $linkvalue = $value;
             $linkvalue =~ s/(\(|\))//g;
-            my $operator = " and " unless $counter == 0;
-            push @link_loop, { link => $linkvalue, operator => $operator };
-            my $separator = C4::Context->preference("authoritysep") unless $counter == 0;
+            if ( $counter != 0 ) {
+                push @link_loop, { link => $linkvalue, operator => ' and ', };
+            } else {
+                push @link_loop, { link => $linkvalue, operator => undef, };
+            }
+            my $separator;
+            if ( $counter != 0 ) {
+                $separator = C4::Context->preference('authoritysep');
+            }
             if ($volume_number) {
                 push @subfields_loop, { volumenum => $value };
             } else {
-                push @subfields_loop, { code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number } unless ( $series_subfield->[0] eq '9' );
+                if ( $series_subfield->[0] ne '9' ) {
+                    push @subfields_loop, {
+                        code      => $code,
+                        value     => $value,
+                        link_loop => \@link_loop,
+                        separator => $separator,
+                        volumenum => $volume_number,
+                    };
+                }
             }
             $counter++;
         }
@@ -2326,8 +2361,11 @@ sub PrepareItemrecordDisplay {
     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
 
     # return nothing if we don't have found an existing framework.
-    return "" unless $tagslib;
-    my $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum ) if ($itemnum);
+    return q{} unless $tagslib;
+    my $itemrecord;
+    if ($itemnum) {
+        $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum );
+    }
     my @loop_data;
     my $authorised_values_sth = $dbh->prepare( "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib" );
     foreach my $tag ( sort keys %{$tagslib} ) {
@@ -2366,15 +2404,20 @@ sub PrepareItemrecordDisplay {
                     && C4::Context->preference('itemcallnumber') ) {
                     my $CNtag      = substr( C4::Context->preference('itemcallnumber'), 0, 3 );
                     my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 );
-                    my $temp = $itemrecord->field($CNtag) if ($itemrecord);
-                    if ($temp) {
-                        $defaultvalue = $temp->subfield($CNsubfield);
+                    if ($itemrecord) {
+                        my $temp = $itemrecord->field($CNtag);
+                        if ($temp) {
+                            $defaultvalue = $temp->subfield($CNsubfield);
+                        }
                     }
                 }
                 if (   $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
                     && $defaultvalues
                     && $defaultvalues->{'callnumber'} ) {
-                    my $temp = $itemrecord->field($subfield) if ($itemrecord);
+                    my $temp;
+                    if ($itemrecord) {
+                        $temp = $itemrecord->field($subfield);
+                    }
                     unless ($temp) {
                         $defaultvalue = $defaultvalues->{'callnumber'} if $defaultvalues;
                     }
@@ -2382,7 +2425,10 @@ sub PrepareItemrecordDisplay {
                 if (   ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' )
                     && $defaultvalues
                     && $defaultvalues->{'branchcode'} ) {
-                    my $temp = $itemrecord->field($subfield) if ($itemrecord);
+                    my $temp;
+                    if ($itemrecord) {
+                        $temp = $itemrecord->field($subfield);
+                    }
                     unless ($temp) {
                         $defaultvalue = $defaultvalues->{branchcode} if $defaultvalues;
                     }
@@ -2507,8 +2553,10 @@ sub PrepareItemrecordDisplay {
             }
         }
     }
-    my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
-      if ( $itemrecord && $itemrecord->field($itemtagfield) );
+    my $itemnumber;
+    if ( $itemrecord && $itemrecord->field($itemtagfield) ) {
+        $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield );
+    }
     return {
         'itemtagfield'    => $itemtagfield,
         'itemtagsubfield' => $itemtagsubfield,
@@ -2655,6 +2703,36 @@ sub GetNoZebraIndexes {
     return %indexes;
 }
 
+=head2 EmbedItemsInMarcBiblio
+
+    EmbedItemsInMarcBiblio($marc, $biblionumber);
+
+Given a MARC::Record object containing a bib record,
+modify it to include the items attached to it as 9XX
+per the bib's MARC framework.
+
+=cut
+
+sub EmbedItemsInMarcBiblio {
+    my ($marc, $biblionumber) = @_;
+    croak "No MARC record" unless $marc;
+
+    my $frameworkcode = GetFrameworkCode($biblionumber);
+    _strip_item_fields($marc, $frameworkcode);
+
+    # ... and embed the current items
+    my $dbh = C4::Context->dbh;
+    my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
+    $sth->execute($biblionumber);
+    my @item_fields;
+    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
+    while (my ($itemnumber) = $sth->fetchrow_array) {
+        my $item_marc = C4::Items::GetMarcItem($biblionumber, $itemnumber);
+        push @item_fields, $item_marc->field($itemtag);
+    }
+    $marc->insert_fields_ordered(@item_fields);
+}
+
 =head1 INTERNAL FUNCTIONS
 
 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
@@ -2993,7 +3071,7 @@ sub _koha_marc_update_bib_ids {
         # drop old field and create new one...
         $old_field = $record->field($biblio_tag);
         $record->delete_field($old_field) if $old_field;
-        $record->append_fields($new_field);
+        $record->insert_fields_ordered($new_field);
 
         # deal with biblioitemnumber
         if ( $biblioitem_tag < 10 ) {
@@ -3409,7 +3487,7 @@ sub ModBiblioMarc {
         }
         substr( $string, 22, 6, "frey50" );
         unless ( $record->subfield( 100, "a" ) ) {
-            $record->insert_grouped_field( MARC::Field->new( 100, "", "", "a" => $string ) );
+            $record->insert_fields_ordered( MARC::Field->new( 100, "", "", "a" => $string ) );
         }
     }