X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FBiblio.pm;h=fa1bb4c84f830bee558a9d6462226051e363dd3e;hb=765535c09ac4cf9c22438e54048f185adfb4a6ac;hp=290efeca9e7d232993d1ea9b4aea5d16f145bb5e;hpb=55107741a2c9f6e9f870f3043b7e20160cfeed3e;p=koha.git diff --git a/C4/Biblio.pm b/C4/Biblio.pm index 290efeca9e..fa1bb4c84f 100644 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -6,24 +6,24 @@ package C4::Biblio; # # This file is part of Koha. # -# Koha is free software; you can redistribute it and/or modify it under the -# terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. # -# Koha is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License along -# with Koha; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . use strict; use warnings; use Carp; -use Encode qw( decode ); +use Encode qw( decode is_utf8 ); use MARC::Record; use MARC::File::USMARC; use MARC::File::XML; @@ -31,7 +31,6 @@ use POSIX qw(strftime); use Module::Load::Conditional qw(can_load); use C4::Koha; -use C4::Dates qw/format_date/; use C4::Log; # logaction use C4::Budgets; use C4::ClassSource; @@ -40,6 +39,8 @@ use C4::Linker; use C4::OAI::Sets; use Koha::Cache; +use Koha::Authority::Types; +use Koha::Acquisition::Currencies; use vars qw($VERSION @ISA @EXPORT); @@ -57,13 +58,14 @@ BEGIN { # to get something push @EXPORT, qw( - &GetBiblio - &GetBiblioData - &GetBiblioItemData - &GetBiblioItemInfosOf - &GetBiblioItemByBiblioNumber - &GetBiblioFromItemNumber - &GetBiblionumberFromItemnumber + GetBiblio + GetBiblioData + GetMarcBiblio + GetBiblioItemData + GetBiblioItemInfosOf + GetBiblioItemByBiblioNumber + GetBiblioFromItemNumber + GetBiblionumberFromItemnumber &GetRecordValue &GetFieldMapping @@ -77,7 +79,6 @@ BEGIN { &GetMarcISBN &GetMarcISSN &GetMarcSubjects - &GetMarcBiblio &GetMarcAuthors &GetMarcSeries &GetMarcHosts @@ -91,6 +92,7 @@ BEGIN { &GetAuthorisedValueDesc &GetMarcStructure + &IsMarcStructureInternal &GetMarcFromKohaField &GetMarcSubfieldStructureFromKohaField &GetFrameworkCode @@ -126,8 +128,8 @@ BEGIN { # Internal functions # those functions are exported but should not be used - # they are usefull is few circumstances, so are exported. - # but don't use them unless you're a core developer ;-) + # they are useful in a few circumstances, so they are exported, + # but don't use them unless you are a core developer ;-) push @EXPORT, qw( &ModBiblioMarc ); @@ -573,8 +575,7 @@ sub LinkBibHeadingsToAuthorities { $results{'linked'}->{ $heading->display_form() }++; } else { - my $authtypedata = - C4::AuthoritiesMarc::GetAuthType( $heading->auth_type() ); + my $authority_type = Koha::Authority::Types->find( $heading->auth_type() ); my $marcrecordauth = MARC::Record->new(); if ( C4::Context->preference('marcflavour') eq 'MARC21' ) { $marcrecordauth->leader(' nz a22 o 4500'); @@ -583,7 +584,7 @@ sub LinkBibHeadingsToAuthorities { $field->delete_subfield( code => '9' ) if defined $current_link; my $authfield = - MARC::Field->new( $authtypedata->{auth_tag_to_report}, + MARC::Field->new( $authority_type->auth_tag_to_report, '', '', "a" => "" . $field->subfield('a') ); map { $authfield->add_subfields( $_->[0] => $_->[1] ) @@ -1081,6 +1082,27 @@ sub GetBiblioItemInfosOf { =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT +=head2 IsMarcStructureInternal + + my $tagslib = C4::Biblio::GetMarcStructure(); + for my $tag ( sort keys %$tagslib ) { + next unless $tag; + for my $subfield ( sort keys %{ $tagslib->{$tag} } ) { + next if IsMarcStructureInternal($tagslib->{$tag}{$subfield}); + } + # Process subfield + } + +GetMarcStructure creates keys (lib, tab, mandatory, repeatable) for a display purpose. +These different values should not be processed as valid subfields. + +=cut + +sub IsMarcStructureInternal { + my ( $subfield ) = @_; + return ref $subfield ? 0 : 1; +} + =head2 GetMarcStructure $res = GetMarcStructure($forlibrarian,$frameworkcode); @@ -1249,36 +1271,66 @@ sub GetMarcSubfieldStructureFromKohaField { =head2 GetMarcBiblio - my $record = GetMarcBiblio($biblionumber, [$embeditems]); + my $record = GetMarcBiblio($biblionumber, [$embeditems], [$opac]); + +Returns MARC::Record representing a biblio record, or C if the +biblionumber doesn't exist. + +=over 4 + +=item C<$biblionumber> -Returns MARC::Record representing bib identified by -C<$biblionumber>. If no bib exists, returns undef. -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. +the biblionumber + +=item C<$embeditems> + +set to true to include item information. + +=item C<$opac> + +set to true to make the result suited for OPAC view. This causes things like +OpacHiddenItems to be applied. + +=back =cut sub GetMarcBiblio { my $biblionumber = shift; my $embeditems = shift || 0; + my $opac = shift || 0; + + if (not defined $biblionumber) { + carp 'GetMarcBiblio called with undefined biblionumber'; + return; + } + my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? "); + my $sth = $dbh->prepare("SELECT biblioitemnumber, marcxml FROM biblioitems WHERE biblionumber=? "); $sth->execute($biblionumber); my $row = $sth->fetchrow_hashref; + my $biblioitemnumber = $row->{'biblioitemnumber'}; my $marcxml = StripNonXmlChars( $row->{'marcxml'} ); + my $frameworkcode = GetFrameworkCode($biblionumber); MARC::File::XML->default_record_format( C4::Context->preference('marcflavour') ); my $record = MARC::Record->new(); if ($marcxml) { - $record = eval { MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour') ) }; + $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::_koha_marc_update_bib_ids($record, '', $biblionumber, $biblionumber); - C4::Biblio::EmbedItemsInMarcBiblio($record, $biblionumber) if ($embeditems); + C4::Biblio::_koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, + $biblioitemnumber ); + C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber, undef, $opac ) + if ($embeditems); return $record; - } else { + } + else { return; } } @@ -1502,10 +1554,10 @@ sub MungeMarcPrice { my ( $price ) = @_; return unless ( $price =~ m/\d/ ); ## No digits means no price. # Look for the currency symbol and the normalized code of the active currency, if it's there, - my $active_currency = C4::Budgets->GetCurrency(); - my $symbol = $active_currency->{'symbol'}; - my $isocode = $active_currency->{'isocode'}; - $isocode = $active_currency->{'currency'} unless defined $isocode; + my $active_currency = Koha::Acquisition::Currencies->get_active; + my $symbol = $active_currency->symbol; + my $isocode = $active_currency->isocode; + $isocode = $active_currency->currency unless defined $isocode; my $localprice; if ( $symbol ) { my @matches =($price=~ / @@ -1513,7 +1565,7 @@ sub MungeMarcPrice { ( # start of capturing parenthesis (?: (?:[\p{Sc}\p{L}\/.]){1,4} # any character from Currency signs or Letter Unicode categories or slash or dot within 1 to 4 occurrences : call this whole block 'symbol block' - |(?:\d+[\p{P}\s]?){1,4} # or else at least one digit followed or not by a punctuation sign or whitespace, all theese within 1 to 4 occurrences : call this whole block 'digits block' + |(?:\d+[\p{P}\s]?){1,4} # or else at least one digit followed or not by a punctuation sign or whitespace, all these within 1 to 4 occurrences : call this whole block 'digits block' ) \s?\p{Sc}?\s? # followed or not by a whitespace. \p{Sc}?\s? are for cases like '25$ USD' (?: @@ -1629,7 +1681,7 @@ sub GetAuthorisedValueDesc { #---- itemtypes if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) { - return getitemtypeinfo($value)->{description}; + return getitemtypeinfo($value)->{translated_description}; } #---- "true" authorized value @@ -1738,10 +1790,11 @@ sub GetMarcISSN { =head2 GetMarcNotes - $marcnotesarray = GetMarcNotes( $record, $marcflavour ); + $marcnotesarray = GetMarcNotes( $record, $marcflavour ); -Get all notes from the MARC record and returns them in an array. -The note are stored in different fields depending on MARC flavour + Get all notes from the MARC record and returns them in an array. + The notes are stored in different fields depending on MARC flavour. + MARC21 field 555 gets special attention for the $u subfields. =cut @@ -1751,38 +1804,28 @@ sub GetMarcNotes { carp 'GetMarcNotes called on undefined record'; return; } - my $scope; - if ( $marcflavour eq "UNIMARC" ) { - $scope = '3..'; - } else { # assume marc21 if not unimarc - $scope = '5..'; - } + + my $scope = $marcflavour eq "UNIMARC"? '3..': '5..'; my @marcnotes; - my $note = ""; - my $tag = ""; - my $marcnote; - my %blacklist = map { $_ => 1 } split(/,/,C4::Context->preference('NotesBlacklist')); + my %blacklist = map { $_ => 1 } + split( /,/, C4::Context->preference('NotesBlacklist')); foreach my $field ( $record->field($scope) ) { my $tag = $field->tag(); - if (!$blacklist{$tag}) { - my $value = $field->as_string(); - if ( $note ne "" ) { - $marcnote = { marcnote => $note, }; - push @marcnotes, $marcnote; - $note = $value; - } - if ( $note ne $value ) { - $note = $note . " " . $value; + next if $blacklist{ $tag }; + if( $marcflavour ne 'UNIMARC' && $tag =~ /555/ ) { + # Field 555$u contains URLs + # We first push the regular subfields and all $u's separately + # Leave further actions to the template + push @marcnotes, { marcnote => $field->as_string('abcd') }; + foreach my $sub ( $field->subfield('u') ) { + push @marcnotes, { marcnote => $sub }; } + } else { + push @marcnotes, { marcnote => $field->as_string() }; } } - - if ($note) { - $marcnote = { marcnote => $note }; - push @marcnotes, $marcnote; #load last tag into array - } return \@marcnotes; -} # end GetMarcNotes +} =head2 GetMarcSubjects @@ -1922,9 +1965,14 @@ sub GetMarcAuthors { } # other subfields + my $unimarc3; for my $authors_subfield (@subfields) { next if ( $authors_subfield->[0] eq '9' ); + # unimarc3 contains the $3 of the author for UNIMARC. + # For french academic libraries, it's the "ppn", and it's required for idref webservice + $unimarc3 = $authors_subfield->[1] if $marcflavour eq 'UNIMARC' and $authors_subfield->[0] =~ /3/; + # don't load unimarc subfields 3, 5 next if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /3|5/ ) ); @@ -1960,6 +2008,7 @@ sub GetMarcAuthors { push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop, authoritylink => $subfield9, + unimarc3 => $unimarc3 }; } return \@marcauthors; @@ -2303,6 +2352,8 @@ $auth_type contains : sub TransformHtmlToXml { my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_; + # NOTE: The parameter $ind_tag is NOT USED -- BZ 11247 + my $xml = MARC::File::XML::header('UTF-8'); $xml .= "\n"; $auth_type = C4::Context->preference('marcflavour') unless $auth_type; @@ -2459,7 +2510,7 @@ sub _default_ind_to_space { =cut sub TransformHtmlToMarc { - my $cgi = shift; + my ($cgi, $isbiblio) = @_; my @params = $cgi->param(); @@ -2470,26 +2521,25 @@ sub TransformHtmlToMarc { foreach my $param_name ( keys %$cgi_params ) { if ( $param_name =~ /^tag_/ ) { my $param_value = $cgi_params->{$param_name}; - if ( $param_value = Encode::decode('UTF-8', $param_value) ) { - $cgi_params->{$param_name} = $param_value; + unless ( Encode::is_utf8( $param_value ) ) { + $cgi_params->{$param_name} = Encode::decode('UTF-8', $param_value ); } - - # FIXME - need to do something if string is not valid UTF-8 } } # creating a new record my $record = MARC::Record->new(); - my $i = 0; my @fields; + my ($biblionumbertagfield, $biblionumbertagsubfield) = (-1, -1); + ($biblionumbertagfield, $biblionumbertagsubfield) = + &GetMarcFromKohaField( "biblio.biblionumber", '' ) if $isbiblio; #FIXME This code assumes that the CGI params will be in the same order as the fields in the template; this is no absolute guarantee! - while ( $params[$i] ) { # browse all CGI params + for (my $i = 0; $params[$i]; $i++ ) { # browse all CGI params my $param = $params[$i]; my $newfield = 0; # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields) if ( $param eq 'biblionumber' ) { - my ( $biblionumbertagfield, $biblionumbertagsubfield ) = &GetMarcFromKohaField( "biblio.biblionumber", '' ); if ( $biblionumbertagfield < 10 ) { $newfield = MARC::Field->new( $biblionumbertagfield, $cgi->param($param), ); } else { @@ -2506,6 +2556,7 @@ sub TransformHtmlToMarc { if ( $tag < 10 ) { # no code for theses fields # in MARC editor, 000 contains the leader. + next if $tag == $biblionumbertagfield; if ( $tag eq '000' ) { # Force a fake leader even if not provided to avoid crashing # during decoding MARC record containing UTF-8 characters @@ -2525,6 +2576,9 @@ sub TransformHtmlToMarc { # browse subfields for this tag (reason for _code_ match) while(defined $params[$j] && $params[$j] =~ /_code_/) { last unless defined $params[$j+1]; + $j += 2 and next + if $tag == $biblionumbertagfield and + $cgi->param($params[$j]) eq $biblionumbertagsubfield; #if next param ne subfield, then it was probably empty #try next param by incrementing j if($params[$j+1]!~/_subfield_/) {$j++; next; } @@ -2542,7 +2596,6 @@ sub TransformHtmlToMarc { } push @fields, $newfield if ($newfield); } - $i++; } $record->append_fields(@fields); @@ -2873,17 +2926,19 @@ sub ModZebra { =head2 EmbedItemsInMarcBiblio - EmbedItemsInMarcBiblio($marc, $biblionumber, $itemnumbers); + EmbedItemsInMarcBiblio($marc, $biblionumber, $itemnumbers, $opac); 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. -if $itemnumbers is defined, only specified itemnumbers are embedded +if $itemnumbers is defined, only specified itemnumbers are embedded. + +If $opac is true, then opac-relevant suppressions are included. =cut sub EmbedItemsInMarcBiblio { - my ($marc, $biblionumber, $itemnumbers) = @_; + my ($marc, $biblionumber, $itemnumbers, $opac) = @_; if ( !$marc ) { carp 'EmbedItemsInMarcBiblio: No MARC record passed'; return; @@ -2900,10 +2955,24 @@ sub EmbedItemsInMarcBiblio { $sth->execute($biblionumber); my @item_fields; my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode ); - while (my ($itemnumber) = $sth->fetchrow_array) { + my @items; + my $opachiddenitems = $opac + && ( C4::Context->preference('OpacHiddenItems') !~ /^\s*$/ ); + require C4::Items; + while ( my ($itemnumber) = $sth->fetchrow_array ) { next if @$itemnumbers and not grep { $_ == $itemnumber } @$itemnumbers; - require C4::Items; - my $item_marc = C4::Items::GetMarcItem($biblionumber, $itemnumber); + my $i = $opachiddenitems ? C4::Items::GetItem($itemnumber) : undef; + push @items, { itemnumber => $itemnumber, item => $i }; + } + my @hiddenitems = + $opachiddenitems + ? C4::Items::GetHiddenItemnumbers( map { $_->{item} } @items ) + : (); + # Convert to a hash for quick searching + my %hiddenitems = map { $_ => 1 } @hiddenitems; + foreach my $itemnumber ( map { $_->{itemnumber} } @items ) { + next if $hiddenitems{$itemnumber}; + my $item_marc = C4::Items::GetMarcItem( $biblionumber, $itemnumber ); push @item_fields, $item_marc->field($itemtag); } $marc->append_fields(@item_fields); @@ -3466,10 +3535,7 @@ sub get_biblio_authorised_values { =head2 CountBiblioInOrders -=over 4 -$count = &CountBiblioInOrders( $biblionumber); - -=back + $count = &CountBiblioInOrders( $biblionumber); This function return count of biblios in orders with $biblionumber @@ -3489,10 +3555,7 @@ sub CountBiblioInOrders { =head2 GetSubscriptionsId -=over 4 -$subscriptions = &GetSubscriptionsId($biblionumber); - -=back + $subscriptions = &GetSubscriptionsId($biblionumber); This function return an array of subscriptionid with $biblionumber @@ -3512,10 +3575,7 @@ sub GetSubscriptionsId { =head2 GetHolds -=over 4 -$holds = &GetHolds($biblionumber); - -=back + $holds = &GetHolds($biblionumber); This function return the count of holds with $biblionumber @@ -3625,7 +3685,7 @@ sub prepare_host_field { if ( $field = $host->field('205') ) { my $s = $field->as_string(); if ($s) { - $sfd{a} = $s; + $sfd{e} = $s; } } #URL