X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=C4%2FBiblio.pm;h=56cd94e0d00b28d787058160b11c7bf9eea0a986;hb=d76dbace5e6436ae5244876b52ffcdc4831f91db;hp=b3a95b898c17281c660ca8468f103b98437322a8;hpb=5baff558a53fabb7b719bf627064cf2c1680f246;p=koha_gimpoz diff --git a/C4/Biblio.pm b/C4/Biblio.pm index b3a95b898c..56cd94e0d0 100644 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -34,6 +34,8 @@ use C4::Dates qw/format_date/; use C4::Log; # logaction use C4::ClassSource; use C4::Charset; +use C4::Linker; +use C4::OAI::Sets; use vars qw($VERSION @ISA @EXPORT); @@ -70,6 +72,7 @@ BEGIN { &GetMarcControlnumber &GetMarcNotes &GetMarcISBN + &GetMarcISSN &GetMarcSubjects &GetMarcBiblio &GetMarcAuthors @@ -110,6 +113,7 @@ BEGIN { # To link headings in a bib record # to authority records. push @EXPORT, qw( + &BiblioAutoLink &LinkBibHeadingsToAuthorities ); @@ -128,6 +132,7 @@ BEGIN { &TransformHtmlToMarc &TransformHtmlToXml &GetNoZebraIndexes + prepare_host_field ); } @@ -265,6 +270,11 @@ sub AddBiblio { # now add the record ModBiblioMarc( $record, $biblionumber, $frameworkcode ) unless $defer_marc_save; + # update OAI-PMH sets + if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) { + C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record); + } + logaction( "CATALOGUING", "ADD", $biblionumber, "biblio" ) if C4::Context->preference("CataloguingLog"); return ( $biblionumber, $biblioitemnumber ); } @@ -336,6 +346,12 @@ sub ModBiblio { # modify the other koha tables _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode ); _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio ); + + # update OAI-PMH sets + if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) { + C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record); + } + return 1; } @@ -414,6 +430,13 @@ sub DelBiblio { C4::Serials::DelSubscription( $subscription->{subscriptionid} ); } + # We delete any existing holds + require C4::Reserves; + my ($count, $reserves) = C4::Reserves::GetReservesFromBiblionumber($biblionumber); + foreach my $res ( @$reserves ) { + C4::Reserves::CancelReserve( $res->{'biblionumber'}, $res->{'itemnumber'}, $res->{'borrowernumber'} ); + } + # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio # for at least 2 reasons : # - we need to read the biblio if NoZebra is set (to remove it from the indexes @@ -449,9 +472,42 @@ sub DelBiblio { return; } + +=head2 BiblioAutoLink + + my $headings_linked = BiblioAutoLink($record, $frameworkcode) + +Automatically links headings in a bib record to authorities. + +=cut + +sub BiblioAutoLink { + my $record = shift; + my $frameworkcode = shift; + my ( $num_headings_changed, %results ); + + my $linker_module = + "C4::Linker::" . ( C4::Context->preference("LinkerModule") || 'Default' ); + eval { eval "require $linker_module"; }; + if ($@) { + $linker_module = 'C4::Linker::Default'; + eval "require $linker_module"; + } + if ($@) { + return 0, 0; + } + + my $linker = $linker_module->new( + { 'options' => C4::Context->preference("LinkerOptions") } ); + my ( $headings_changed, undef ) = + LinkBibHeadingsToAuthorities( $linker, $record, $frameworkcode, C4::Context->preference("CatalogModuleRelink") || '' ); + # By default we probably don't want to relink things when cataloging + return $headings_changed; +} + =head2 LinkBibHeadingsToAuthorities - my $headings_linked = LinkBibHeadingsToAuthorities($marc); + my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode, [$allowrelink]); Links bib headings to authority records by checking each authority-controlled field in the C @@ -459,9 +515,9 @@ object C<$marc>, looking for a matching authority record, and setting the linking subfield $9 to the ID of that authority record. -If no matching authority exists, or if multiple -authorities match, no $9 will be added, and any -existing one inthe field will be deleted. +If $allowrelink is false, existing authids will never be +replaced, regardless of the values of LinkerKeepStale and +LinkerRelink. Returns the number of heading links changed in the MARC record. @@ -469,38 +525,112 @@ MARC record. =cut sub LinkBibHeadingsToAuthorities { + my $linker = shift; + my $bib = shift; + my $frameworkcode = shift; + my $allowrelink = shift; + my %results; require C4::Heading; - my $bib = shift; + require C4::AuthoritiesMarc; + $allowrelink = 1 unless defined $allowrelink; my $num_headings_changed = 0; foreach my $field ( $bib->fields() ) { - my $heading = C4::Heading->new_from_bib_field($field); + my $heading = C4::Heading->new_from_bib_field( $field, $frameworkcode ); next unless defined $heading; # check existing $9 my $current_link = $field->subfield('9'); - # look for matching authorities - my $authorities = $heading->authorities(); + if ( defined $current_link && (!$allowrelink || !C4::Context->preference('LinkerRelink')) ) + { + $results{'linked'}->{ $heading->display_form() }++; + next; + } - # want only one exact match - if ( $#{$authorities} == 0 ) { - my $authority = MARC::Record->new_from_usmarc( $authorities->[0] ); - my $authid = $authority->field('001')->data(); - next if defined $current_link and $current_link eq $authid; + my ( $authid, $fuzzy ) = $linker->get_link($heading); + if ($authid) { + $results{ $fuzzy ? 'fuzzy' : 'linked' } + ->{ $heading->display_form() }++; + next if defined $current_link and $current_link == $authid; $field->delete_subfield( code => '9' ) if defined $current_link; $field->add_subfields( '9', $authid ); $num_headings_changed++; - } else { - if ( defined $current_link ) { + } + else { + if ( defined $current_link + && (!$allowrelink || C4::Context->preference('LinkerKeepStale')) ) + { + $results{'fuzzy'}->{ $heading->display_form() }++; + } + elsif ( C4::Context->preference('AutoCreateAuthorities') ) { + my $authtypedata = + C4::AuthoritiesMarc::GetAuthType( $heading->auth_type() ); + my $marcrecordauth = MARC::Record->new(); + if ( C4::Context->preference('marcflavour') eq 'MARC21' ) { + $marcrecordauth->leader(' nz a22 o 4500'); + SetMarcUnicodeFlag( $marcrecordauth, 'MARC21' ); + } + my $authfield = + MARC::Field->new( $authtypedata->{auth_tag_to_report}, + '', '', "a" => "" . $field->subfield('a') ); + map { + $authfield->add_subfields( $_->[0] => $_->[1] ) + if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a" ) + } $field->subfields(); + $marcrecordauth->insert_fields_ordered($authfield); + +# bug 2317: ensure new authority knows it's using UTF-8; currently +# only need to do this for MARC21, as MARC::Record->as_xml_record() handles +# automatically for UNIMARC (by not transcoding) +# FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record +# use UTF-8, but as of 2008-08-05, did not want to introduce that kind +# of change to a core API just before the 3.0 release. + + if ( C4::Context->preference('marcflavour') eq 'MARC21' ) { + $marcrecordauth->insert_fields_ordered( + MARC::Field->new( + '667', '', '', + 'a' => "Machine generated authority record." + ) + ); + my $cite = + $bib->author() . ", " + . $bib->title_proper() . ", " + . $bib->publication_date() . " "; + $cite =~ s/^[\s\,]*//; + $cite =~ s/[\s\,]*$//; + $cite = + "Work cat.: (" + . C4::Context->preference('MARCOrgCode') . ")" + . $bib->subfield( '999', 'c' ) . ": " + . $cite; + $marcrecordauth->insert_fields_ordered( + MARC::Field->new( '670', '', '', 'a' => $cite ) ); + } + + # warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted; + + $authid = + C4::AuthoritiesMarc::AddAuthority( $marcrecordauth, '', + $heading->auth_type() ); + $field->add_subfields( '9', $authid ); + $num_headings_changed++; + $results{'added'}->{ $heading->display_form() }++; + } + elsif ( defined $current_link ) { $field->delete_subfield( code => '9' ); $num_headings_changed++; + $results{'unlinked'}->{ $heading->display_form() }++; + } + else { + $results{'unlinked'}->{ $heading->display_form() }++; } } } - return $num_headings_changed; + return $num_headings_changed, \%results; } =head2 GetRecordValue @@ -951,7 +1081,7 @@ sub GetMarcStructure { } $sth = $dbh->prepare( - "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue + "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue,maxlength FROM marc_subfield_structure WHERE frameworkcode=? ORDER BY tagfield,tagsubfield @@ -970,10 +1100,12 @@ sub GetMarcStructure { my $isurl; my $link; my $defaultvalue; + my $maxlength; while ( ( $tag, $subfield, $liblibrarian, $libopac, $tab, $mandatory, $repeatable, $authorised_value, - $authtypecode, $value_builder, $kohafield, $seealso, $hidden, $isurl, $link, $defaultvalue + $authtypecode, $value_builder, $kohafield, $seealso, $hidden, $isurl, $link, $defaultvalue, + $maxlength ) = $sth->fetchrow ) { @@ -990,6 +1122,7 @@ sub GetMarcStructure { $res->{$tag}->{$subfield}->{isurl} = $isurl; $res->{$tag}->{$subfield}->{'link'} = $link; $res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue; + $res->{$tag}->{$subfield}->{maxlength} = $maxlength; } $marc_structure_cache->{$forlibrarian}->{$frameworkcode} = $res; @@ -1374,9 +1507,9 @@ Get the control number / record Identifier from the MARC record and return it. sub GetMarcControlnumber { my ( $record, $marcflavour ) = @_; my $controlnumber = ""; - # Control number or Record identifier are the same field in MARC21 and UNIMARC + # Control number or Record identifier are the same field in MARC21, UNIMARC and NORMARC # Keep $marcflavour for possible later use - if ($marcflavour eq "MARC21" || $marcflavour eq "UNIMARC") { + if ($marcflavour eq "MARC21" || $marcflavour eq "UNIMARC" || $marcflavour eq "NORMARC") { my $controlnumberField = $record->field('001'); if ($controlnumberField) { $controlnumber = $controlnumberField->data(); @@ -1390,7 +1523,7 @@ sub GetMarcControlnumber { $marcisbnsarray = GetMarcISBN( $record, $marcflavour ); Get all ISBNs from the MARC record and returns them in an array. -ISBNs stored in differents places depending on MARC flavour +ISBNs stored in different fields depending on MARC flavour =cut @@ -1425,12 +1558,38 @@ sub GetMarcISBN { return \@marcisbns; } # end GetMarcISBN + +=head2 GetMarcISSN + + $marcissnsarray = GetMarcISSN( $record, $marcflavour ); + +Get all valid ISSNs from the MARC record and returns them in an array. +ISSNs are stored in different fields depending on MARC flavour + +=cut + +sub GetMarcISSN { + my ( $record, $marcflavour ) = @_; + my $scope; + if ( $marcflavour eq "UNIMARC" ) { + $scope = '011'; + } + else { # assume MARC21 or NORMARC + $scope = '022'; + } + my @marcissns; + foreach my $field ( $record->field($scope) ) { + push @marcissns, $field->subfield( 'a' ); + } + return \@marcissns; +} # end GetMarcISSN + =head2 GetMarcNotes $marcnotesarray = GetMarcNotes( $record, $marcflavour ); Get all notes from the MARC record and returns them in an array. -The note are stored in differents places depending on MARC flavour +The note are stored in different fields depending on MARC flavour =cut @@ -1470,7 +1629,7 @@ sub GetMarcNotes { $marcsubjcts = GetMarcSubjects($record,$marcflavour); Get all subjects from the MARC record and returns them in an array. -The subjects are stored in differents places depending on MARC flavour +The subjects are stored in different fields depending on MARC flavour =cut @@ -1530,7 +1689,7 @@ sub GetMarcSubjects { # ignore $9 my @this_link_loop = @link_loop; - push @subfields_loop, { code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator } unless ( $subject_subfield->[0] eq 9 ); + push @subfields_loop, { code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator } unless ( $subject_subfield->[0] eq 9 || $subject_subfield->[0] eq '0' ); $counter++; } @@ -1545,7 +1704,7 @@ sub GetMarcSubjects { authors = GetMarcAuthors($record,$marcflavour); Get all authors from the MARC record and returns them in an array. -The authors are stored in differents places depending on MARC flavour +The authors are stored in different fields depending on MARC flavour =cut @@ -1614,7 +1773,7 @@ sub GetMarcAuthors { link_loop => \@this_link_loop, separator => $separator } - unless ( $authors_subfield->[0] eq '9' ); + unless ( $authors_subfield->[0] eq '9' || $authors_subfield->[0] eq '0'); $count_auth++; } push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop }; @@ -1682,7 +1841,7 @@ sub GetMarcUrls { $marcseriesarray = GetMarcSeries($record,$marcflavour); Get all series from the MARC record and returns them in an array. -The series are stored in differents places depending on MARC flavour +The series are stored in different fields depending on MARC flavour =cut @@ -1864,6 +2023,7 @@ sub PrepHostMarcField { my ($hostbiblionumber,$hostitemnumber, $marcflavour) = @_; $marcflavour ||="MARC21"; + require C4::Items; my $hostrecord = GetMarcBiblio($hostbiblionumber); my $item = C4::Items::GetItem($hostitemnumber); @@ -2876,45 +3036,6 @@ sub _AddBiblioNoZebra { return %result; } -=head2 _find_value - - ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding); - -Find the given $subfield in the given $tag in the given -MARC::Record $record. If the subfield is found, returns -the (indicators, value) pair; otherwise, (undef, undef) is -returned. - -PROPOSITION : -Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities. -I suggest we export it from this module. - -=cut - -sub _find_value { - my ( $tagfield, $insubfield, $record, $encoding ) = @_; - my @result; - my $indicator; - if ( $tagfield < 10 ) { - if ( $record->field($tagfield) ) { - push @result, $record->field($tagfield)->data(); - } else { - push @result, ""; - } - } else { - foreach my $field ( $record->field($tagfield) ) { - my @subfields = $field->subfields(); - foreach my $subfield (@subfields) { - if ( @$subfield[0] eq $insubfield ) { - push @result, @$subfield[1]; - $indicator = $field->indicator(1) . $field->indicator(2); - } - } - } - } - return ( $indicator, @result ); -} - =head2 _koha_marc_update_bib_ids @@ -3347,9 +3468,12 @@ Function exported, but should NOT be used, unless you really know what you're do =cut sub ModBiblioMarc { - - # pass the MARC::Record to this function, and it will create the records in the marc field + # pass the MARC::Record to this function, and it will create the records in + # the marc field my ( $record, $biblionumber, $frameworkcode ) = @_; + + # Clone record as it gets modified + $record = $record->clone(); my $dbh = C4::Context->dbh; my @fields = $record->fields(); if ( !$frameworkcode ) { @@ -3530,9 +3654,134 @@ sub GetHolds { return ($holds); } +=head2 prepare_host_field + +$marcfield = prepare_host_field( $hostbiblioitem, $marcflavour ); +Generate the host item entry for an analytic child entry + +=cut + +sub prepare_host_field { + my ( $hostbiblio, $marcflavour ) = @_; + $marcflavour ||= C4::Context->preference('marcflavour'); + my $host = GetMarcBiblio($hostbiblio); + # unfortunately as_string does not 'do the right thing' + # if field returns undef + my %sfd; + my $field; + my $host_field; + if ( $marcflavour eq 'MARC21' || $marcflavour eq 'NORMARC' ) { + if ( $field = $host->field('100') || $host->field('110') || $host->field('11') ) { + my $s = $field->as_string('ab'); + if ($s) { + $sfd{a} = $s; + } + } + if ( $field = $host->field('245') ) { + my $s = $field->as_string('a'); + if ($s) { + $sfd{t} = $s; + } + } + if ( $field = $host->field('260') ) { + my $s = $field->as_string('abc'); + if ($s) { + $sfd{d} = $s; + } + } + if ( $field = $host->field('240') ) { + my $s = $field->as_string(); + if ($s) { + $sfd{b} = $s; + } + } + if ( $field = $host->field('022') ) { + my $s = $field->as_string('a'); + if ($s) { + $sfd{x} = $s; + } + } + if ( $field = $host->field('020') ) { + my $s = $field->as_string('a'); + if ($s) { + $sfd{z} = $s; + } + } + if ( $field = $host->field('001') ) { + $sfd{w} = $field->data(),; + } + $host_field = MARC::Field->new( 773, '0', ' ', %sfd ); + return $host_field; + } + elsif ( $marcflavour eq 'UNIMARC' ) { + #author + if ( $field = $host->field('700') || $host->field('710') || $host->field('720') ) { + my $s = $field->as_string('ab'); + if ($s) { + $sfd{a} = $s; + } + } + #title + if ( $field = $host->field('200') ) { + my $s = $field->as_string('a'); + if ($s) { + $sfd{t} = $s; + } + } + #place of publicaton + if ( $field = $host->field('210') ) { + my $s = $field->as_string('a'); + if ($s) { + $sfd{c} = $s; + } + } + #date of publication + if ( $field = $host->field('210') ) { + my $s = $field->as_string('d'); + if ($s) { + $sfd{d} = $s; + } + } + #edition statement + if ( $field = $host->field('205') ) { + my $s = $field->as_string(); + if ($s) { + $sfd{a} = $s; + } + } + #URL + if ( $field = $host->field('856') ) { + my $s = $field->as_string('u'); + if ($s) { + $sfd{u} = $s; + } + } + #ISSN + if ( $field = $host->field('011') ) { + my $s = $field->as_string('a'); + if ($s) { + $sfd{x} = $s; + } + } + #ISBN + if ( $field = $host->field('010') ) { + my $s = $field->as_string('a'); + if ($s) { + $sfd{y} = $s; + } + } + if ( $field = $host->field('001') ) { + $sfd{0} = $field->data(),; + } + $host_field = MARC::Field->new( 461, '0', ' ', %sfd ); + return $host_field; + } + return; +} 1; + __END__ =head1 AUTHOR