# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
+
+use vars qw(@ISA @EXPORT);
+BEGIN {
+ require Exporter;
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw(
+ AddBiblio
+ GetBiblioData
+ GetMarcBiblio
+ GetRecordValue
+ GetISBDView
+ GetMarcControlnumber
+ GetMarcNotes
+ GetMarcISBN
+ GetMarcISSN
+ GetMarcSubjects
+ GetMarcAuthors
+ GetMarcSeries
+ GetMarcHosts
+ GetMarcUrls
+ GetUsedMarcStructure
+ GetXmlBiblio
+ GetCOinSBiblio
+ GetMarcPrice
+ MungeMarcPrice
+ GetMarcQuantity
+ GetAuthorisedValueDesc
+ GetMarcStructure
+ IsMarcStructureInternal
+ GetMarcFromKohaField
+ GetMarcSubfieldStructureFromKohaField
+ GetFrameworkCode
+ TransformKohaToMarc
+ PrepHostMarcField
+ CountItemsIssued
+ CountBiblioInOrders
+ ModBiblio
+ ModZebra
+ UpdateTotalIssues
+ RemoveAllNsb
+ DelBiblio
+ BiblioAutoLink
+ LinkBibHeadingsToAuthorities
+ TransformMarcToKoha
+ TransformHtmlToMarc
+ TransformHtmlToXml
+ prepare_host_field
+ );
+
+ # Internal functions
+ # those functions are exported but should not be used
+ # 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
+ );
+}
+
use Carp;
use Encode qw( decode is_utf8 );
+use List::MoreUtils qw( uniq );
use MARC::Record;
use MARC::File::USMARC;
use MARC::File::XML;
use Koha::Caches;
use Koha::Authority::Types;
use Koha::Acquisition::Currencies;
-use Koha::Biblio::Metadata;
use Koha::Biblio::Metadatas;
use Koha::Holds;
use Koha::ItemTypes;
use Koha::SearchEngine;
use Koha::Libraries;
+use Koha::Util::MARC;
-use vars qw(@ISA @EXPORT);
use vars qw($debug $cgi_debug);
-BEGIN {
-
- require Exporter;
- @ISA = qw( Exporter );
-
- # to add biblios
- # EXPORTED FUNCTIONS.
- push @EXPORT, qw(
- &AddBiblio
- );
-
- # to get something
- push @EXPORT, qw(
- GetBiblioData
- GetMarcBiblio
- GetBiblioItemData
- GetBiblioItemInfosOf
- GetBiblioItemByBiblioNumber
-
- &GetRecordValue
-
- &GetISBDView
-
- &GetMarcControlnumber
- &GetMarcNotes
- &GetMarcISBN
- &GetMarcISSN
- &GetMarcSubjects
- &GetMarcAuthors
- &GetMarcSeries
- &GetMarcHosts
- GetMarcUrls
- &GetUsedMarcStructure
- &GetXmlBiblio
- &GetCOinSBiblio
- &GetMarcPrice
- &MungeMarcPrice
- &GetMarcQuantity
-
- &GetAuthorisedValueDesc
- &GetMarcStructure
- &IsMarcStructureInternal
- &GetMarcFromKohaField
- &GetMarcSubfieldStructureFromKohaField
- &GetFrameworkCode
- &TransformKohaToMarc
- &PrepHostMarcField
-
- &CountItemsIssued
- &CountBiblioInOrders
- );
-
- # To modify something
- push @EXPORT, qw(
- &ModBiblio
- &ModZebra
- &UpdateTotalIssues
- &RemoveAllNsb
- );
-
- # To delete something
- push @EXPORT, qw(
- &DelBiblio
- );
-
- # To link headings in a bib record
- # to authority records.
- push @EXPORT, qw(
- &BiblioAutoLink
- &LinkBibHeadingsToAuthorities
- );
-
- # Internal functions
- # those functions are exported but should not be used
- # 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
- );
-
- # Others functions
- push @EXPORT, qw(
- &TransformMarcToKoha
- &TransformHtmlToMarc
- &TransformHtmlToXml
- prepare_host_field
- );
-}
=head1 NAME
$defer_marc_save = 1;
}
+ if (C4::Context->preference('BiblioAddsAuthorities')) {
+ BiblioAutoLink( $record, $frameworkcode );
+ }
+
my ( $biblionumber, $biblioitemnumber, $error );
my $dbh = C4::Context->dbh;
logaction( "CATALOGUING", "MODIFY", $biblionumber, "biblio BEFORE=>" . $newrecord->as_formatted );
}
+ if (C4::Context->preference('BiblioAddsAuthorities')) {
+ BiblioAutoLink( $record, $frameworkcode );
+ }
+
# 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()) {
sub DelBiblio {
my ($biblionumber) = @_;
+
+ my $biblio = Koha::Biblios->find( $biblionumber );
+ return unless $biblio; # Should we throw an exception instead?
+
my $dbh = C4::Context->dbh;
my $error; # for error handling
}
# We delete any existing holds
- my $biblio = Koha::Biblios->find( $biblionumber );
my $holds = $biblio->holds;
while ( my $hold = $holds->next ) {
$hold->cancel;
'', '', "a" => "" . $field->subfield('a') );
map {
$authfield->add_subfields( $_->[0] => $_->[1] )
- if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a" )
+ if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a"
+ && C4::Heading::valid_bib_heading_subfield(
+ $authority_type->auth_tag_to_report, $_->[0] )
+ );
} $field->subfields();
$marcrecordauth->insert_fields_ordered($authfield);
return ($data);
} # sub GetBiblioData
-=head2 &GetBiblioItemData
-
- $itemdata = &GetBiblioItemData($biblioitemnumber);
-
-Looks up the biblioitem with the given biblioitemnumber. Returns a
-reference-to-hash. The keys are the fields from the C<biblio>,
-C<biblioitems>, and C<itemtypes> tables in the Koha database, except
-that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
-
-=cut
-
-#'
-sub GetBiblioItemData {
- my ($biblioitemnumber) = @_;
- my $dbh = C4::Context->dbh;
- my $query = "SELECT *,biblioitems.notes AS bnotes
- FROM biblio LEFT JOIN biblioitems on biblio.biblionumber=biblioitems.biblionumber ";
- unless ( C4::Context->preference('item-level_itypes') ) {
- $query .= "LEFT JOIN itemtypes on biblioitems.itemtype=itemtypes.itemtype ";
- }
- $query .= " WHERE biblioitemnumber = ? ";
- my $sth = $dbh->prepare($query);
- my $data;
- $sth->execute($biblioitemnumber);
- $data = $sth->fetchrow_hashref;
- $sth->finish;
- return ($data);
-} # sub &GetBiblioItemData
-
-=head2 GetBiblioItemByBiblioNumber
-
-NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.
-
-=cut
-
-sub GetBiblioItemByBiblioNumber {
- my ($biblionumber) = @_;
- my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare("Select * FROM biblioitems WHERE biblionumber = ?");
- my $count = 0;
- my @results;
-
- $sth->execute($biblionumber);
-
- while ( my $data = $sth->fetchrow_hashref ) {
- push @results, $data;
- }
-
- $sth->finish;
- return @results;
-}
-
=head2 GetISBDView
$isbd = &GetISBDView({
my $framework = $params->{framework};
my $itemtype = $framework;
my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch", $itemtype );
- my $tagslib = &GetMarcStructure( 1, $itemtype, { unsafe => 1 } );
+ my $tagslib = GetMarcStructure( 1, $itemtype, { unsafe => 1 } );
my $ISBD = C4::Context->preference($sysprefname);
my $bloc = $ISBD;
return $res;
}
-=head2 GetBiblioItemInfosOf
-
- GetBiblioItemInfosOf(@biblioitemnumbers);
-
-=cut
-
-sub GetBiblioItemInfosOf {
- my @biblioitemnumbers = @_;
-
- my $biblioitemnumber_values = @biblioitemnumbers ? join( ',', @biblioitemnumbers ) : "''";
-
- my $dbh = C4::Context->dbh;
- my $query = "
- SELECT biblioitemnumber,
- publicationyear,
- itemtype
- FROM biblioitems
- WHERE biblioitemnumber IN ($biblioitemnumber_values)
- ";
- return $dbh->selectall_hashref($query, 'biblioitemnumber');
-}
-
=head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
=head2 IsMarcStructureInternal
my $dbh = C4::Context->dbh;
my $sth = $dbh->prepare(
- "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable
+ "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable,ind1_defaultvalue,ind2_defaultvalue
FROM marc_tag_structure
WHERE frameworkcode=?
ORDER BY tagfield"
);
$sth->execute($frameworkcode);
- my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
+ my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable, $ind1_defaultvalue, $ind2_defaultvalue );
- while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) = $sth->fetchrow ) {
+ while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable, $ind1_defaultvalue, $ind2_defaultvalue ) = $sth->fetchrow ) {
$res->{$tag}->{lib} = ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
$res->{$tag}->{tab} = "";
$res->{$tag}->{mandatory} = $mandatory;
$res->{$tag}->{repeatable} = $repeatable;
+ $res->{$tag}->{ind1_defaultvalue} = $ind1_defaultvalue;
+ $res->{$tag}->{ind2_defaultvalue} = $ind2_defaultvalue;
}
$sth = $dbh->prepare(
return $sth->fetchall_arrayref( {} );
}
+=pod
+
=head2 GetMarcSubfieldStructure
+ my $structure = GetMarcSubfieldStructure($frameworkcode, [$params]);
+
+Returns a reference to hash representing MARC subfield structure
+for framework with framework code C<$frameworkcode>, C<$params> is
+optional and may contain additional options.
+
+=over 4
+
+=item C<$frameworkcode>
+
+The framework code.
+
+=item C<$params>
+
+An optional hash reference with additional options.
+The following options are supported:
+
+=over 4
+
+=item unsafe
+
+Pass { unsafe => 1 } do disable cached object cloning,
+and instead get a shared reference, resulting in better
+performance (but care must be taken so that retured object
+is never modified).
+
+Note: If you call GetMarcSubfieldStructure with unsafe => 1, do not modify or
+even autovivify its contents. It is a cached/shared data structure. Your
+changes would be passed around in subsequent calls.
+
+=back
+
+=back
+
=cut
sub GetMarcSubfieldStructure {
- my ( $frameworkcode ) = @_;
+ my ( $frameworkcode, $params ) = @_;
$frameworkcode //= '';
my $cache = Koha::Caches->get_instance();
my $cache_key = "MarcSubfieldStructure-$frameworkcode";
- my $cached = $cache->get_from_cache($cache_key);
+ my $cached = $cache->get_from_cache($cache_key, { unsafe => ($params && $params->{unsafe}) });
return $cached if $cached;
my $dbh = C4::Context->dbh;
=head2 GetMarcFromKohaField
- ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
+ ( $field,$subfield ) = GetMarcFromKohaField( $kohafield );
+ @fields = GetMarcFromKohaField( $kohafield );
+ $field = GetMarcFromKohaField( $kohafield );
-Returns the MARC fields & subfields mapped to the koha field
-for the given frameworkcode or default framework if $frameworkcode is missing
+ Returns the MARC fields & subfields mapped to $kohafield.
+ Since the Default framework is considered as authoritative for such
+ mappings, the former frameworkcode parameter is obsoleted.
+
+ In list context all mappings are returned; there can be multiple
+ mappings. Note that in the above example you could miss a second
+ mappings in the first call.
+ In scalar context only the field tag of the first mapping is returned.
=cut
sub GetMarcFromKohaField {
- my ( $kohafield, $frameworkcode ) = @_;
- return (0, undef) unless $kohafield;
- my $mss = GetMarcSubfieldStructure( $frameworkcode );
+ my ( $kohafield ) = @_;
+ return unless $kohafield;
+ # The next call uses the Default framework since it is AUTHORITATIVE
+ # for all Koha to MARC mappings.
+ my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
my @retval;
foreach( @{ $mss->{$kohafield} } ) {
push @retval, $_->{tagfield}, $_->{tagsubfield};
}
- return wantarray ? @retval : $retval[0];
+ return wantarray ? @retval : ( @retval ? $retval[0] : undef );
}
=head2 GetMarcSubfieldStructureFromKohaField
- my $subfield_structure = &GetMarcSubfieldStructureFromKohaField($kohafield, $frameworkcode);
-
-Returns a hashref where keys are marc_subfield_structure column names for the
-row where kohafield=$kohafield for the given framework code.
-In list context returns a list of those hashrefs in case duplicate Koha to
-MARC mappings exist.
+ my $str = GetMarcSubfieldStructureFromKohaField( $kohafield );
-$frameworkcode is optional. If not given, then the default framework is used.
+ Returns marc subfield structure information for $kohafield.
+ The Default framework is used, since it is authoritative for kohafield
+ mappings.
+ In list context returns a list of all hashrefs, since there may be
+ multiple mappings. In scalar context the first hashref is returned.
=cut
sub GetMarcSubfieldStructureFromKohaField {
- my ( $kohafield, $frameworkcode ) = @_;
+ my ( $kohafield ) = @_;
return unless $kohafield;
- my $mss = GetMarcSubfieldStructure( $frameworkcode );
+ # The next call uses the Default framework since it is AUTHORITATIVE
+ # for all Koha to MARC mappings.
+ my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
return unless $mss->{$kohafield};
return wantarray ? @{$mss->{$kohafield}} : $mss->{$kohafield}->[0];
}
my $record = GetMarcBiblio({
biblionumber => $biblionumber,
embed_items => $embeditems,
- opac => $opac });
+ opac => $opac,
+ borcat => $patron_category });
Returns MARC::Record representing a biblio record, or C<undef> if the
biblionumber doesn't exist.
set to true to make the result suited for OPAC view. This causes things like
OpacHiddenItems to be applied.
+=item C<$borcat>
+
+If the OpacHiddenItemsExceptions system preference is set, this patron category
+can be used to make visible OPAC items which would be normally hidden.
+It only makes sense in combination both embed_items and opac values true.
+
=back
=cut
my $biblionumber = $params->{biblionumber};
my $embeditems = $params->{embed_items} || 0;
my $opac = $params->{opac} || 0;
+ my $borcat = $params->{borcat} // q{};
if (not defined $biblionumber) {
carp 'GetMarcBiblio called with undefined biblionumber';
C4::Biblio::_koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber,
$biblioitemnumber );
- C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber, undef, $opac )
+ C4::Biblio::EmbedItemsInMarcBiblio({
+ marc_record => $record,
+ biblionumber => $biblionumber,
+ opac => $opac,
+ borcat => $borcat })
if ($embeditems);
return $record;
FROM biblio_metadata
WHERE biblionumber=?
AND format='marcxml'
- AND marcflavour=?
+ AND `schema`=?
|, undef, $biblionumber, C4::Context->preference('marcflavour')
);
return $marcxml;
#---- branch
if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
- return Koha::Libraries->find($value)->branchname;
+ my $branch = Koha::Libraries->find($value);
+ return $branch? $branch->branchname: q{};
}
#---- itemtypes
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.
+ MARC21 5XX $u subfields receive special attention as they are URIs.
=cut
foreach my $field ( $record->field($scope) ) {
my $tag = $field->tag();
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') };
+ if( $marcflavour ne 'UNIMARC' && $field->subfield('u') ) {
+ # Field 5XX$u always contains URI
+ # Examples: 505u, 506u, 510u, 514u, 520u, 530u, 538u, 540u, 542u, 552u, 555u, 561u, 563u, 583u
+ # We first push the other subfields, then all $u's separately
+ # Leave further actions to the template (see e.g. opac-detail)
+ my $othersub =
+ join '', ( 'a' .. 't', 'v' .. 'z', '0' .. '9' ); # excl 'u'
+ push @marcnotes, { marcnote => $field->as_string($othersub) };
foreach my $sub ( $field->subfield('u') ) {
+ $sub =~ s/^\s+|\s+$//g; # trim
push @marcnotes, { marcnote => $sub };
}
} else {
=head2 TransformKohaToMarc
- $record = TransformKohaToMarc( $hash )
+ $record = TransformKohaToMarc( $hash [, $params ] )
-This function builds partial MARC::Record from a hash
-Hash entries can be from biblio or biblioitems.
+This function builds a (partial) MARC::Record from a hash.
+Hash entries can be from biblio, biblioitems or items.
+The params hash includes the parameter no_split used in C4::Items.
This function is called in acquisition module, to create a basic catalogue
-entry from user entry
+entry from user entry.
=cut
sub TransformKohaToMarc {
- my ( $hash, $frameworkcode, $params ) = @_;
+ my ( $hash, $params ) = @_;
my $record = MARC::Record->new();
SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
- # NOTE: Many older calls do not include a frameworkcode. In that case
- # we default to Default framework.
- my $mss = GetMarcSubfieldStructure( $frameworkcode//'' );
+
+ # In the next call we use the Default framework, since it is considered
+ # authoritative for Koha to Marc mappings.
+ my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # do not change framewok
my $tag_hr = {};
while ( my ($kohafield, $value) = each %$hash ) {
foreach my $fld ( @{ $mss->{$kohafield} } ) {
my ($hostbiblionumber,$hostitemnumber, $marcflavour) = @_;
$marcflavour ||="MARC21";
- require C4::Items;
my $hostrecord = GetMarcBiblio({ biblionumber => $hostbiblionumber });
- my $item = C4::Items::GetItem($hostitemnumber);
-
+ my $item = Koha::Items->find($hostitemnumber);
+
my $hostmarcfield;
if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
#other fields
my $ed = $hostrecord->subfield('250','a');
- my $barcode = $item->{'barcode'};
+ my $barcode = $item->barcode;
my $title = $hostrecord->subfield('245','a');
# record control number, 001 with 003 and prefix
my $prevtag = -1;
my $first = 1;
my $j = -1;
+ my $close_last_tag;
for ( my $i = 0 ; $i < @$tags ; $i++ ) {
if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a" ) {
@$values[$i] =~ s/'/'/g;
if ( ( @$tags[$i] ne $prevtag ) ) {
+ $close_last_tag = 0;
$j++ unless ( @$tags[$i] eq "" );
my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) };
my $indicator2 = eval { substr( @$indicator[$j], 1, 1 ) };
$xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
$xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
$first = 0;
+ $close_last_tag = 1;
} else {
$first = 1;
}
$xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
$xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
$first = 0;
+ $close_last_tag = 1;
}
}
}
if ($first) {
$xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
$first = 0;
+ $close_last_tag = 1;
}
$xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
}
}
$prevtag = @$tags[$i];
}
- $xml .= "</datafield>\n" if $xml =~ m/<datafield/;
+ $xml .= "</datafield>\n" if $close_last_tag;
if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist ) {
# warn "SETTING 100 for $auth_type";
=head2 TransformMarcToKoha
- $result = TransformMarcToKoha( $record, $frameworkcode )
+ $result = TransformMarcToKoha( $record, undef, $limit )
Extract data from a MARC bib record into a hashref representing
-Koha biblio, biblioitems, and items fields.
+Koha biblio, biblioitems, and items fields.
If passed an undefined record will log the error and return an empty
-hash_ref
+hash_ref.
=cut
sub TransformMarcToKoha {
my ( $record, $frameworkcode, $limit_table ) = @_;
+ # FIXME Parameter $frameworkcode is obsolete and will be removed
+ $limit_table //= q{};
my $result = {};
if (!defined $record) {
carp('TransformMarcToKoha called with undefined record');
return $result;
}
- $limit_table = $limit_table || 0;
- $frameworkcode //= '';
-
- my $inverted_field_map = _get_inverted_marc_field_map($frameworkcode);
- my %tables = ();
- if ( defined $limit_table && $limit_table eq 'items' ) {
- $tables{'items'} = 1;
- } else {
- $tables{'items'} = 1;
- $tables{'biblio'} = 1;
- $tables{'biblioitems'} = 1;
+ my %tables = ( biblio => 1, biblioitems => 1, items => 1 );
+ if( $limit_table eq 'items' ) {
+ %tables = ( items => 1 );
}
- # traverse through record
- MARCFIELD: foreach my $field ( $record->fields() ) {
- my $tag = $field->tag();
- next MARCFIELD unless exists $inverted_field_map->{$tag};
- if ( $field->is_control_field() ) {
- my $kohafields = $inverted_field_map->{$tag}->{list};
- ENTRY: foreach my $entry ( @{$kohafields} ) {
- my ( $subfield, $table, $column ) = @{$entry};
- my $value = $field->data;
- next ENTRY unless exists $tables{$table};
- next ENTRY if !$value;
- my $key = _disambiguate( $table, $column );
- if ( $result->{$key} ) {
- $result->{$key} .= " | " . $value
- unless $result->{$key} eq $value;
- } else {
- $result->{$key} = $value;
- }
- }
- } else {
-
- # deal with subfields
- MARCSUBFIELD: foreach my $sf ( $field->subfields() ) {
- my $code = $sf->[0];
- next MARCSUBFIELD unless exists $inverted_field_map->{$tag}->{sfs}->{$code};
- my $value = $sf->[1];
- SFENTRY: foreach my $entry ( @{ $inverted_field_map->{$tag}->{sfs}->{$code} } ) {
- my ( $table, $column ) = @{$entry};
- next SFENTRY unless exists $tables{$table};
- next SFENTRY if !$value;
- my $key = _disambiguate( $table, $column );
- if ( $result->{$key} ) {
- $result->{$key} .= " | " . $value
- unless $result->{$key} eq $value;
- } else {
- $result->{$key} = $value;
- }
- }
- }
- }
- }
-
- # modify copyrightdate to keep only the 1st year found
- if ( exists $result->{'copyrightdate'} ) {
- my $temp = $result->{'copyrightdate'};
- $temp =~ m/c(\d\d\d\d)/;
- if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
- $result->{'copyrightdate'} = $1;
- } else { # if no cYYYY, get the 1st date.
- $temp =~ m/(\d\d\d\d)/;
- $result->{'copyrightdate'} = $1;
- }
- }
-
- # modify publicationyear to keep only the 1st year found
- if ( exists $result->{'publicationyear'} ) {
- my $temp = $result->{'publicationyear'};
- if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
- $result->{'publicationyear'} = $1;
- } else { # if no cYYYY, get the 1st date.
- $temp =~ m/(\d\d\d\d)/;
- $result->{'publicationyear'} = $1;
- }
- }
-
- return $result;
-}
-
-sub _get_inverted_marc_field_map {
- my ( $frameworkcode ) = @_;
- my $field_map = {};
- my $mss = GetMarcSubfieldStructure( $frameworkcode );
-
+ # The next call acknowledges Default as the authoritative framework
+ # for Koha to MARC mappings.
+ my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
foreach my $kohafield ( keys %{ $mss } ) {
- foreach my $fld ( @{ $mss->{$kohafield} } ) {
- my $tag = $fld->{tagfield};
- my $subfield = $fld->{tagsubfield};
- my ( $table, $column ) = split /[.]/, $kohafield, 2;
- push @{ $field_map->{$tag}->{list} },
- [ $subfield, $table, $column ];
- push @{ $field_map->{$tag}->{sfs}->{$subfield} },
- [ $table, $column ];
- }
+ my ( $table, $column ) = split /[.]/, $kohafield, 2;
+ next unless $tables{$table};
+ my $val = TransformMarcToKohaOneField( $kohafield, $record );
+ next if !defined $val;
+ my $key = _disambiguate( $table, $column );
+ $result->{$key} = $val;
}
- return $field_map;
+ return $result;
}
=head2 _disambiguate
=cut
-sub CountItemsIssued {
- my ($biblionumber) = @_;
- my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
- $sth->execute($biblionumber);
- my $row = $sth->fetchrow_hashref();
- return $row->{'issuedCount'};
-}
-
sub _disambiguate {
my ( $table, $column ) = @_;
if ( $column eq "cn_sort" or $column eq "cn_source" ) {
=head2 TransformMarcToKohaOneField
- $value = TransformMarcToKohaOneField( 'biblio.title', $record, $frameworkcode );
+ $val = TransformMarcToKohaOneField( 'biblio.title', $marc );
+
+ Note: The authoritative Default framework is used implicitly.
=cut
sub TransformMarcToKohaOneField {
- my ( $kohafield, $marc, $frameworkcode ) = @_;
-
- # It is not really useful to repeat all code from TransformMarcToKoha here.
- # Since it is fast enough (by caching), we just extract the field.
- my $koharec = TransformMarcToKoha( $marc, $frameworkcode );
- my @temp = split /\./, $kohafield, 2;
- $kohafield = _disambiguate( @temp ) if @temp > 1;
- return $koharec ? $koharec->{$kohafield} : undef;
+ my ( $kohafield, $marc ) = @_;
+
+ my ( @rv, $retval );
+ my @mss = GetMarcSubfieldStructureFromKohaField($kohafield);
+ foreach my $fldhash ( @mss ) {
+ my $tag = $fldhash->{tagfield};
+ my $sub = $fldhash->{tagsubfield};
+ foreach my $fld ( $marc->field($tag) ) {
+ if( $sub eq '@' || $fld->is_control_field ) {
+ push @rv, $fld->data if $fld->data;
+ } else {
+ push @rv, grep { $_ } $fld->subfield($sub);
+ }
+ }
+ }
+ return unless @rv;
+ $retval = join ' | ', uniq(@rv);
+
+ # Additional polishing for individual kohafields
+ if( $kohafield =~ /copyrightdate|publicationyear/ ) {
+ $retval = _adjust_pubyear( $retval );
+ }
+
+ return $retval;
+}
+
+=head2 _adjust_pubyear
+
+ Helper routine for TransformMarcToKohaOneField
+
+=cut
+
+sub _adjust_pubyear {
+ my $retval = shift;
+ # modify return value to keep only the 1st year found
+ if( $retval =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
+ $retval = $1;
+ } elsif( $retval =~ m/(\d\d\d\d)/ && $1 > 0 ) {
+ $retval = $1;
+ } elsif( $retval =~ m/
+ (?<year>\d)[-]?[.Xx?]{3}
+ |(?<year>\d{2})[.Xx?]{2}
+ |(?<year>\d{3})[.Xx?]
+ |(?<year>\d)[-]{3}\?
+ |(?<year>\d\d)[-]{2}\?
+ |(?<year>\d{3})[-]\?
+ /xms ) { # the form 198-? occurred in Dutch ISBD rules
+ my $digits = $+{year};
+ $retval = $digits * ( 10 ** ( 4 - length($digits) ));
+ }
+ return $retval;
+}
+
+=head2 CountItemsIssued
+
+ my $count = CountItemsIssued( $biblionumber );
+
+=cut
+
+sub CountItemsIssued {
+ my ($biblionumber) = @_;
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
+ $sth->execute($biblionumber);
+ my $row = $sth->fetchrow_hashref();
+ return $row->{'issuedCount'};
}
=head2 ModZebra
=head2 EmbedItemsInMarcBiblio
- EmbedItemsInMarcBiblio($marc, $biblionumber, $itemnumbers, $opac);
+ EmbedItemsInMarcBiblio({
+ marc_record => $marc,
+ biblionumber => $biblionumber,
+ item_numbers => $itemnumbers,
+ opac => $opac });
Given a MARC::Record object containing a bib record,
modify it to include the items attached to it as 9XX
If $opac is true, then opac-relevant suppressions are included.
+If opac filtering will be done, borcat should be passed to properly
+override if necessary.
+
=cut
sub EmbedItemsInMarcBiblio {
- my ($marc, $biblionumber, $itemnumbers, $opac) = @_;
+ my ($params) = @_;
+ my ($marc, $biblionumber, $itemnumbers, $opac, $borcat);
+ $marc = $params->{marc_record};
if ( !$marc ) {
carp 'EmbedItemsInMarcBiblio: No MARC record passed';
return;
}
+ $biblionumber = $params->{biblionumber};
+ $itemnumbers = $params->{item_numbers};
+ $opac = $params->{opac};
+ $borcat = $params->{borcat} // q{};
$itemnumbers = [] unless defined $itemnumbers;
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 );
- my @items;
+
+ my @item_fields; # Array holding the actual MARC data for items to be included.
+ my @items; # Array holding items which are both in the list (sitenumbers)
+ # and on this biblionumber
+
+ # Flag indicating if there is potential hiding.
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;
- my $i = $opachiddenitems ? C4::Items::GetItem($itemnumber) : undef;
- push @items, { itemnumber => $itemnumber, item => $i };
+ my $item;
+ if ( $opachiddenitems ) {
+ $item = Koha::Items->find($itemnumber);
+ $item = $item ? $item->unblessed : undef;
+ }
+ push @items, { itemnumber => $itemnumber, item => $item };
}
+ my @items2pass = map { $_->{item} } @items;
my @hiddenitems =
$opachiddenitems
- ? C4::Items::GetHiddenItemnumbers( map { $_->{item} } @items )
+ ? C4::Items::GetHiddenItemnumbers({
+ items => \@items2pass,
+ borcat => $borcat })
: ();
# Convert to a hash for quick searching
my %hiddenitems = map { $_ => 1 } @hiddenitems;
$sth->execute(
$frameworkcode, $biblio->{'author'}, $biblio->{'title'}, $biblio->{'unititle'}, $biblio->{'notes'},
- $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}, $biblio->{'biblionumber'}
+ $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'} ? int($biblio->{'copyrightdate'}) : undef, $biblio->{'abstract'}, $biblio->{'biblionumber'}
) if $biblio->{'biblionumber'};
if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
$schema->txn_do(
sub {
$dbh->do( q|
- INSERT INTO deletedbiblio_metadata (biblionumber, format, marcflavour, metadata)
- SELECT biblionumber, format, marcflavour, metadata FROM biblio_metadata WHERE biblionumber=?
+ INSERT INTO deletedbiblio_metadata (biblionumber, format, `schema`, metadata)
+ SELECT biblionumber, format, `schema`, metadata FROM biblio_metadata WHERE biblionumber=?
|, undef, $biblionumber );
$dbh->do( q|DELETE FROM biblio_metadata WHERE biblionumber=?|,
undef, $biblionumber );
my $metadata = {
biblionumber => $biblionumber,
format => 'marcxml',
- marcflavour => C4::Context->preference('marcflavour'),
+ schema => C4::Context->preference('marcflavour'),
};
- # FIXME To replace with ->find_or_create?
- if ( my $m_rs = Koha::Biblio::Metadatas->find($metadata) ) {
- $m_rs->metadata( $record->as_xml_record($encoding) );
- $m_rs->store;
- } else {
- my $m_rs = Koha::Biblio::Metadata->new($metadata);
- $m_rs->metadata( $record->as_xml_record($encoding) );
- $m_rs->store;
+ $record->as_usmarc; # Bug 20126/10455 This triggers field length calculation
+
+ my $m_rs = Koha::Biblio::Metadatas->find($metadata) //
+ Koha::Biblio::Metadata->new($metadata);
+
+ my $userenv = C4::Context->userenv;
+ if ($userenv) {
+ my $borrowernumber = $userenv->{number};
+ my $borrowername = join ' ', map { $_ // q{} } @$userenv{qw(firstname surname)};
+ unless ($m_rs->in_storage) {
+ Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForCreatorId'), $borrowernumber);
+ Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForCreatorName'), $borrowername);
+ }
+ Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForModifierId'), $borrowernumber);
+ Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForModifierName'), $borrowername);
}
- ModZebra( $biblionumber, "specialUpdate", "biblioserver", $record );
+
+ $m_rs->metadata( $record->as_xml_record($encoding) );
+ $m_rs->store;
+
+ ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
return $biblionumber;
}