X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FBiblio.pm;h=3a34d79524937455f6ca34521536afee5029ae5a;hb=e1775fe1d5ffae3411d64c4d69fca75ad5641293;hp=ec4c71ebedff39819128bb5466ebc20b3970c8d3;hpb=cf2eb49448a5bf8f84d3ae550b9cdcba88ef5c93;p=koha.git diff --git a/C4/Biblio.pm b/C4/Biblio.pm index ec4c71ebed..3a34d79524 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 utf8; +use Encode qw( decode is_utf8 ); use MARC::Record; use MARC::File::USMARC; use MARC::File::XML; @@ -31,20 +31,23 @@ 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; 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($VERSION @ISA @EXPORT); +use vars qw(@ISA @EXPORT); +use vars qw($debug $cgi_debug); BEGIN { - $VERSION = 3.07.00.049; require Exporter; @ISA = qw( Exporter ); @@ -57,13 +60,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 +81,6 @@ BEGIN { &GetMarcISBN &GetMarcISSN &GetMarcSubjects - &GetMarcBiblio &GetMarcAuthors &GetMarcSeries &GetMarcHosts @@ -91,6 +94,7 @@ BEGIN { &GetAuthorisedValueDesc &GetMarcStructure + &IsMarcStructureInternal &GetMarcFromKohaField &GetMarcSubfieldStructureFromKohaField &GetFrameworkCode @@ -126,8 +130,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 ); @@ -256,7 +260,7 @@ sub AddBiblio { # transform the data into koha-table style data SetUTF8Flag($record); - my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode ); + my $olddata = TransformMarcToKoha( $record, $frameworkcode ); ( $biblionumber, $error ) = _koha_add_biblio( $dbh, $olddata, $frameworkcode ); $olddata->{'biblionumber'} = $biblionumber; ( $biblioitemnumber, $error ) = _koha_add_biblioitem( $dbh, $olddata ); @@ -339,7 +343,7 @@ sub ModBiblio { _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber ); # load the koha-table data object - my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode ); + my $oldbiblio = TransformMarcToKoha( $record, $frameworkcode ); # update MARC subfield that stores biblioitems.cn_sort _koha_marc_update_biblioitem_cn_sort( $record, $oldbiblio, $frameworkcode ); @@ -573,8 +577,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 +586,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] ) @@ -924,12 +927,14 @@ Return the ISBD view which can be included in opac and intranet sub GetISBDView { my ( $biblionumber, $template ) = @_; my $record = GetMarcBiblio($biblionumber, 1); + $template ||= ''; + my $sysprefname = $template eq 'opac' ? 'opacisbd' : 'isbd'; return unless defined $record; my $itemtype = &GetFrameworkCode($biblionumber); my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch", $itemtype ); my $tagslib = &GetMarcStructure( 1, $itemtype ); - my $ISBD = C4::Context->preference('isbd'); + my $ISBD = C4::Context->preference($sysprefname); my $bloc = $ISBD; my $res; my $blocres; @@ -1081,6 +1086,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); @@ -1093,7 +1119,6 @@ $frameworkcode : the framework code to read sub GetMarcStructure { my ( $forlibrarian, $frameworkcode ) = @_; - my $dbh = C4::Context->dbh; $frameworkcode = "" unless $frameworkcode; $forlibrarian = $forlibrarian ? 1 : 0; @@ -1102,6 +1127,7 @@ sub GetMarcStructure { my $cached = $cache->get_from_cache($cache_key); return $cached if $cached; + my $dbh = C4::Context->dbh; my $sth = $dbh->prepare( "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable FROM marc_tag_structure @@ -1183,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); @@ -1205,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 @@ -1227,58 +1275,78 @@ $frameworkcode is optional. If not given, then the default framework is used. =cut sub GetMarcSubfieldStructureFromKohaField { - my ($kohafield, $frameworkcode) = @_; + my ( $kohafield, $frameworkcode ) = @_; - return undef unless $kohafield; - $frameworkcode //= ''; + return unless $kohafield; - 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 $result; + my $mss = GetMarcSubfieldStructure( $frameworkcode ); + return exists $mss->{$kohafield} + ? $mss->{$kohafield} + : undef; } =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> + +the biblionumber + +=item C<$embeditems> + +set to true to include item information. -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. +=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 +1570,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 +1581,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' (?: @@ -1616,7 +1684,6 @@ descriptions rather than normal ones when they exist. sub GetAuthorisedValueDesc { my ( $tag, $subfield, $value, $framework, $tagslib, $category, $opac ) = @_; - my $dbh = C4::Context->dbh; if ( !$category ) { @@ -1629,13 +1696,14 @@ sub GetAuthorisedValueDesc { #---- itemtypes if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) { - return getitemtypeinfo($value)->{description}; + return getitemtypeinfo($value)->{translated_description}; } #---- "true" authorized value $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'}; } + my $dbh = C4::Context->dbh; if ( $category ne "" ) { my $sth = $dbh->prepare( "SELECT lib, lib_opac FROM authorised_values WHERE category = ? AND authorised_value = ?" ); $sth->execute( $category, $value ); @@ -1738,10 +1806,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 +1820,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 +1981,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 +2024,7 @@ sub GetMarcAuthors { push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop, authoritylink => $subfield9, + unimarc3 => $unimarc3 }; } return \@marcauthors; @@ -2178,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) { @@ -2303,6 +2369,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; @@ -2336,9 +2404,6 @@ sub TransformHtmlToXml { @$values[$i] =~ s/"/"/g; @$values[$i] =~ s/'/'/g; - # if ( !utf8::is_utf8( @$values[$i] ) ) { - # utf8::decode( @$values[$i] ); - # } if ( ( @$tags[$i] ne $prevtag ) ) { $j++ unless ( @$tags[$i] eq "" ); my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) }; @@ -2462,9 +2527,9 @@ sub _default_ind_to_space { =cut sub TransformHtmlToMarc { - my $cgi = shift; + 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 @@ -2473,30 +2538,29 @@ sub TransformHtmlToMarc { foreach my $param_name ( keys %$cgi_params ) { if ( $param_name =~ /^tag_/ ) { my $param_value = $cgi_params->{$param_name}; - if ( utf8::decode($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), ); + $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_..." @@ -2509,18 +2573,20 @@ 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 @@ -2528,16 +2594,20 @@ 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; } + 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 @@ -2545,19 +2615,15 @@ sub TransformHtmlToMarc { } push @fields, $newfield if ($newfield); } - $i++; } $record->append_fields(@fields); return $record; } -# cache inverted MARC field map -our $inverted_field_map; - =head2 TransformMarcToKoha - $result = TransformMarcToKoha( $dbh, $record, $frameworkcode ) + $result = TransformMarcToKoha( $record, $frameworkcode ) Extract data from a MARC bib record into a hashref representing Koha biblio, biblioitems, and items fields. @@ -2568,7 +2634,7 @@ hash_ref =cut sub TransformMarcToKoha { - my ( $dbh, $record, $frameworkcode, $limit_table ) = @_; + my ( $record, $frameworkcode, $limit_table ) = @_; my $result = {}; if (!defined $record) { @@ -2578,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' ) { @@ -2594,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}; @@ -2614,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 ); @@ -2659,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; } @@ -2837,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; @@ -2876,17 +2968,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; @@ -2903,10 +2997,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); @@ -3401,78 +3509,13 @@ 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; - 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 -=over 4 -$count = &CountBiblioInOrders( $biblionumber); - -=back + $count = &CountBiblioInOrders( $biblionumber); This function return count of biblios in orders with $biblionumber @@ -3492,10 +3535,7 @@ sub CountBiblioInOrders { =head2 GetSubscriptionsId -=over 4 -$subscriptions = &GetSubscriptionsId($biblionumber); - -=back + $subscriptions = &GetSubscriptionsId($biblionumber); This function return an array of subscriptionid with $biblionumber @@ -3515,10 +3555,7 @@ sub GetSubscriptionsId { =head2 GetHolds -=over 4 -$holds = &GetHolds($biblionumber); - -=back + $holds = &GetHolds($biblionumber); This function return the count of holds with $biblionumber @@ -3628,7 +3665,7 @@ sub prepare_host_field { if ( $field = $host->field('205') ) { my $s = $field->as_string(); if ($s) { - $sfd{a} = $s; + $sfd{e} = $s; } } #URL @@ -3684,16 +3721,26 @@ sub UpdateTotalIssues { my ($biblionumber, $increase, $value) = @_; my $totalissues; + my $record = GetMarcBiblio($biblionumber); + unless ($record) { + carp "UpdateTotalIssues could not get biblio record"; + return; + } my $data = GetBiblioData($biblionumber); + unless ($data) { + carp "UpdateTotalIssues could not get datas of biblio"; + return; + } + my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField('biblioitems.totalissues', $data->{'frameworkcode'}); + unless ($totalissuestag) { + return 1; # There is nothing to do + } if (defined $value) { $totalissues = $value; } else { $totalissues = $data->{'totalissues'} + $increase; } - my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField('biblioitems.totalissues', $data->{'frameworkcode'}); - - my $record = GetMarcBiblio($biblionumber); my $field = $record->field($totalissuestag); if (defined $field) { @@ -3704,8 +3751,7 @@ sub UpdateTotalIssues { $record->insert_grouped_field($field); } - ModBiblio($record, $biblionumber, $data->{'frameworkcode'}); - return; + return ModBiblio($record, $biblionumber, $data->{'frameworkcode'}); } =head2 RemoveAllNsb