# 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
+ GetISBDView
+ GetMarcControlnumber
+ GetMarcISBN
+ GetMarcISSN
+ GetMarcSubjects
+ GetMarcAuthors
+ GetMarcSeries
+ GetMarcUrls
+ GetUsedMarcStructure
+ GetXmlBiblio
+ GetMarcPrice
+ MungeMarcPrice
+ GetMarcQuantity
+ GetAuthorisedValueDesc
+ GetMarcStructure
+ IsMarcStructureInternal
+ GetMarcFromKohaField
+ GetMarcSubfieldStructureFromKohaField
+ GetFrameworkCode
+ TransformKohaToMarc
+ PrepHostMarcField
+ CountItemsIssued
+ 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 Try::Tiny;
use Encode qw( decode is_utf8 );
+use List::MoreUtils qw( uniq );
use MARC::Record;
use MARC::File::USMARC;
use MARC::File::XML;
use C4::Charset;
use C4::Linker;
use C4::OAI::Sets;
-use C4::Debug;
+use Koha::Logger;
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::Plugins;
use Koha::SearchEngine;
+use Koha::SearchEngine::Indexer;
use Koha::Libraries;
-
-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
- );
-}
+use Koha::Util::MARC;
=head1 NAME
$defer_marc_save = 1;
}
- my ( $biblionumber, $biblioitemnumber, $error );
- my $dbh = C4::Context->dbh;
+ my $schema = Koha::Database->schema;
+ my ( $biblionumber, $biblioitemnumber );
+ try {
+ $schema->txn_do(sub {
+
+ # transform the data into koha-table style data
+ SetUTF8Flag($record);
+ my $olddata = TransformMarcToKoha( $record, $frameworkcode );
+
+ my $biblio = Koha::Biblio->new(
+ {
+ frameworkcode => $frameworkcode,
+ author => $olddata->{author},
+ title => $olddata->{title},
+ subtitle => $olddata->{subtitle},
+ medium => $olddata->{medium},
+ part_number => $olddata->{part_number},
+ part_name => $olddata->{part_name},
+ unititle => $olddata->{unititle},
+ notes => $olddata->{notes},
+ serial =>
+ ( $olddata->{serial} || $olddata->{seriestitle} ? 1 : 0 ),
+ seriestitle => $olddata->{seriestitle},
+ copyrightdate => $olddata->{copyrightdate},
+ datecreated => \'NOW()',
+ abstract => $olddata->{abstract},
+ }
+ )->store;
+ $biblionumber = $biblio->biblionumber;
+ Koha::Exceptions::ObjectNotCreated->throw unless $biblio;
+
+ my ($cn_sort) = GetClassSort( $olddata->{'biblioitems.cn_source'}, $olddata->{'cn_class'}, $olddata->{'cn_item'} );
+ my $biblioitem = Koha::Biblioitem->new(
+ {
+ biblionumber => $biblionumber,
+ volume => $olddata->{volume},
+ number => $olddata->{number},
+ itemtype => $olddata->{itemtype},
+ isbn => $olddata->{isbn},
+ issn => $olddata->{issn},
+ publicationyear => $olddata->{publicationyear},
+ publishercode => $olddata->{publishercode},
+ volumedate => $olddata->{volumedate},
+ volumedesc => $olddata->{volumedesc},
+ collectiontitle => $olddata->{collectiontitle},
+ collectionissn => $olddata->{collectionissn},
+ collectionvolume => $olddata->{collectionvolume},
+ editionstatement => $olddata->{editionstatement},
+ editionresponsibility => $olddata->{editionresponsibility},
+ illus => $olddata->{illus},
+ pages => $olddata->{pages},
+ notes => $olddata->{bnotes},
+ size => $olddata->{size},
+ place => $olddata->{place},
+ lccn => $olddata->{lccn},
+ url => $olddata->{url},
+ cn_source => $olddata->{'biblioitems.cn_source'},
+ cn_class => $olddata->{cn_class},
+ cn_item => $olddata->{cn_item},
+ cn_suffix => $olddata->{cn_suff},
+ cn_sort => $cn_sort,
+ totalissues => $olddata->{totalissues},
+ ean => $olddata->{ean},
+ agerestriction => $olddata->{agerestriction},
+ }
+ )->store;
+ Koha::Exceptions::ObjectNotCreated->throw unless $biblioitem;
+ $biblioitemnumber = $biblioitem->biblioitemnumber;
- # transform the data into koha-table style data
- SetUTF8Flag($record);
- my $olddata = TransformMarcToKoha( $record, $frameworkcode );
- ( $biblionumber, $error ) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
- $olddata->{'biblionumber'} = $biblionumber;
- ( $biblioitemnumber, $error ) = _koha_add_biblioitem( $dbh, $olddata );
+ _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber );
- _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber );
+ # update MARC subfield that stores biblioitems.cn_sort
+ _koha_marc_update_biblioitem_cn_sort( $record, $olddata, $frameworkcode );
- # update MARC subfield that stores biblioitems.cn_sort
- _koha_marc_update_biblioitem_cn_sort( $record, $olddata, $frameworkcode );
+ if (C4::Context->preference('BiblioAddsAuthorities')) {
+ BiblioAutoLink( $record, $frameworkcode );
+ }
- # now add the record
- ModBiblioMarc( $record, $biblionumber, $frameworkcode ) unless $defer_marc_save;
+ # now add the record
+ ModBiblioMarc( $record, $biblionumber ) unless $defer_marc_save;
- # update OAI-PMH sets
- if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
- C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
- }
+ # 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");
+ _after_biblio_action_hooks({ action => 'create', biblio_id => $biblionumber });
+
+ logaction( "CATALOGUING", "ADD", $biblionumber, "biblio" ) if C4::Context->preference("CataloguingLog");
+ });
+ } catch {
+ warn $_;
+ ( $biblionumber, $biblioitemnumber ) = ( undef, undef );
+ };
return ( $biblionumber, $biblioitemnumber );
}
=head2 ModBiblio
- ModBiblio( $record,$biblionumber,$frameworkcode);
+ ModBiblio( $record,$biblionumber,$frameworkcode, $disable_autolink);
Replace an existing bib record identified by C<$biblionumber>
with one supplied by the MARC::Record object C<$record>. The embedded
which fields are used to store embedded item, biblioitem,
and biblionumber data for indexing.
+Unless C<$disable_autolink> is passed ModBiblio will relink record headings
+to authorities based on settings in the system preferences. This flag allows
+us to not relink records when the authority linker is saving modifications.
+
Returns 1 on success 0 on failure
=cut
sub ModBiblio {
- my ( $record, $biblionumber, $frameworkcode ) = @_;
+ my ( $record, $biblionumber, $frameworkcode, $disable_autolink ) = @_;
if (!$record) {
carp 'No record passed to ModBiblio';
return 0;
logaction( "CATALOGUING", "MODIFY", $biblionumber, "biblio BEFORE=>" . $newrecord->as_formatted );
}
+ if ( !$disable_autolink && 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()) {
_koha_marc_update_biblioitem_cn_sort( $record, $oldbiblio, $frameworkcode );
# update the MARC record (that now contains biblio and items) with the new record data
- &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
+ &ModBiblioMarc( $record, $biblionumber );
# modify the other koha tables
_koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
_koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
+ _after_biblio_action_hooks({ action => 'modify', biblio_id => $biblionumber });
+
# update OAI-PMH sets
if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
my $record = shift;
my $frameworkcode = shift;
# get the items before and append them to the biblio before updating the record, atm we just have the biblio
- my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
+ my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
# delete any item fields from incoming record to avoid
# duplication or incorrect data - use AddItem() or ModItem()
=cut
sub DelBiblio {
- my ($biblionumber) = @_;
+ my ($biblionumber, $params) = @_;
+
+ 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
return $error if $error;
- # We delete attached subscriptions
- require C4::Serials;
- my $subscriptions = C4::Serials::GetFullSubscriptionsFromBiblionumber($biblionumber);
- foreach my $subscription (@$subscriptions) {
- C4::Serials::DelSubscription( $subscription->{subscriptionid} );
- }
-
# We delete any existing holds
- my $biblio = Koha::Biblios->find( $biblionumber );
my $holds = $biblio->holds;
while ( my $hold = $holds->next ) {
$hold->cancel;
}
- # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
- # for at least 2 reasons :
- # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
- # and we would have no way to remove it (except manually in zebra, but I bet it would be very hard to handle the problem)
- ModZebra( $biblionumber, "recordDelete", "biblioserver" );
+ unless ( $params->{skip_record_index} ){
+ my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
+ $indexer->index_records( $biblionumber, "recordDelete", "biblioserver" );
+ }
# delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
$sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
# from being generated by _koha_delete_biblioitems
$error = _koha_delete_biblio( $dbh, $biblionumber );
+ _after_biblio_action_hooks({ action => 'delete', biblio_id => $biblionumber });
+
logaction( "CATALOGUING", "DELETE", $biblionumber, "biblio" ) if C4::Context->preference("CataloguingLog");
return;
sub BiblioAutoLink {
my $record = shift;
my $frameworkcode = shift;
+ my $verbose = shift;
if (!$record) {
carp('Undefined record passed to BiblioAutoLink');
return 0;
my $linker = $linker_module->new(
{ 'options' => C4::Context->preference("LinkerOptions") } );
- my ( $headings_changed, undef ) =
- LinkBibHeadingsToAuthorities( $linker, $record, $frameworkcode, C4::Context->preference("CatalogModuleRelink") || '' );
+ my ( $headings_changed, $results ) =
+ LinkBibHeadingsToAuthorities( $linker, $record, $frameworkcode, C4::Context->preference("CatalogModuleRelink") || '', undef, $verbose );
# By default we probably don't want to relink things when cataloging
- return $headings_changed;
+ return $headings_changed, $results;
}
=head2 LinkBibHeadingsToAuthorities
- my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode, [$allowrelink]);
+ my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode, [$allowrelink, $tagtolink, $verbose]);
Links bib headings to authority records by checking
each authority-controlled field in the C<MARC::Record>
my $bib = shift;
my $frameworkcode = shift;
my $allowrelink = shift;
+ my $tagtolink = shift;
+ my $verbose = shift;
my %results;
if (!$bib) {
carp 'LinkBibHeadingsToAuthorities called on undefined bib record';
$allowrelink = 1 unless defined $allowrelink;
my $num_headings_changed = 0;
foreach my $field ( $bib->fields() ) {
- my $heading = C4::Heading->new_from_bib_field( $field, $frameworkcode );
+ if ( defined $tagtolink ) {
+ next unless $field->tag() == $tagtolink ;
+ }
+ my $heading = C4::Heading->new_from_field( $field, $frameworkcode );
next unless defined $heading;
# check existing $9
if ( defined $current_link && (!$allowrelink || !C4::Context->preference('LinkerRelink')) )
{
$results{'linked'}->{ $heading->display_form() }++;
+ push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
next;
}
- my ( $authid, $fuzzy ) = $linker->get_link($heading);
+ my ( $authid, $fuzzy, $match_count ) = $linker->get_link($heading);
if ($authid) {
$results{ $fuzzy ? 'fuzzy' : 'linked' }
->{ $heading->display_form() }++;
- next if defined $current_link and $current_link == $authid;
+ if(defined $current_link and $current_link == $authid) {
+ push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
+ next;
+ }
$field->delete_subfield( code => '9' ) if defined $current_link;
$field->add_subfields( '9', $authid );
$num_headings_changed++;
+ push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => 'LOCAL_FOUND'}) if $verbose;
}
else {
+ my $authority_type = Koha::Authority::Types->find( $heading->auth_type() );
if ( defined $current_link
&& (!$allowrelink || C4::Context->preference('LinkerKeepStale')) )
{
$results{'fuzzy'}->{ $heading->display_form() }++;
+ push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
}
elsif ( C4::Context->preference('AutoCreateAuthorities') ) {
if ( _check_valid_auth_link( $current_link, $field ) ) {
$results{'linked'}->{ $heading->display_form() }++;
}
- else {
+ elsif ( !$match_count ) {
my $authority_type = Koha::Authority::Types->find( $heading->auth_type() );
my $marcrecordauth = MARC::Record->new();
if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
}
$field->delete_subfield( code => '9' )
if defined $current_link;
- my $authfield =
- MARC::Field->new( $authority_type->auth_tag_to_report,
- '', '', "a" => "" . $field->subfield('a') );
- map {
- $authfield->add_subfields( $_->[0] => $_->[1] )
- if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a" )
- } $field->subfields();
+ my @auth_subfields;
+ foreach my $subfield ( $field->subfields() ){
+ if ( $subfield->[0] =~ /[A-z]/
+ && C4::Heading::valid_heading_subfield(
+ $field->tag, $subfield->[0] )
+ ){
+ push @auth_subfields, $subfield->[0] => $subfield->[1];
+ }
+ }
+ # Bib headings contain some ending punctuation that should NOT
+ # be included in the authority record. Strip those before creation
+ next unless @auth_subfields; # Don't try to create a record if we have no fields;
+ my $last_sub = pop @auth_subfields;
+ $last_sub =~ s/[\s]*[,.:=;!%\/][\s]*$//;
+ push @auth_subfields, $last_sub;
+ my $authfield = MARC::Field->new( $authority_type->auth_tag_to_report, '', '', @auth_subfields );
$marcrecordauth->insert_fields_ordered($authfield);
# bug 2317: ensure new authority knows it's using UTF-8; currently
$num_headings_changed++;
$linker->update_cache($heading, $authid);
$results{'added'}->{ $heading->display_form() }++;
+ push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => 'CREATED'}) if $verbose;
}
}
elsif ( defined $current_link ) {
if ( _check_valid_auth_link( $current_link, $field ) ) {
$results{'linked'}->{ $heading->display_form() }++;
+ push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => 'UNCHANGED'}) if $verbose;
}
else {
$field->delete_subfield( code => '9' );
$num_headings_changed++;
$results{'unlinked'}->{ $heading->display_form() }++;
+ push(@{$results{'details'}}, { tag => $field->tag(), authid => undef, status => 'NONE_FOUND', auth_type => $heading->auth_type(), tag_to_report => $authority_type->auth_tag_to_report}) if $verbose;
}
}
else {
$results{'unlinked'}->{ $heading->display_form() }++;
+ push(@{$results{'details'}}, { tag => $field->tag(), authid => undef, status => 'NONE_FOUND', auth_type => $heading->auth_type(), tag_to_report => $authority_type->auth_tag_to_report}) if $verbose;
}
}
}
+ push(@{$results{'details'}}, { tag => '', authid => undef, status => 'UNCHANGED'}) unless %results;
return $num_headings_changed, \%results;
}
sub _check_valid_auth_link {
my ( $authid, $field ) = @_;
-
require C4::AuthoritiesMarc;
- my $authorized_heading =
- C4::AuthoritiesMarc::GetAuthorizedHeading( { 'authid' => $authid } ) || '';
-
- return ($field->as_string('abcdefghijklmnopqrstuvwxyz') eq $authorized_heading);
-}
-
-=head2 GetRecordValue
-
- my $values = GetRecordValue($field, $record, $frameworkcode);
-
-Get MARC fields from a keyword defined in fieldmapping table.
-
-=cut
-
-sub GetRecordValue {
- my ( $field, $record, $frameworkcode ) = @_;
-
- if (!$record) {
- carp 'GetRecordValue called with undefined record';
- return;
- }
- my $dbh = C4::Context->dbh;
-
- my $sth = $dbh->prepare('SELECT fieldcode, subfieldcode FROM fieldmapping WHERE frameworkcode = ? AND field = ?');
- $sth->execute( $frameworkcode, $field );
-
- my @result = ();
-
- while ( my $row = $sth->fetchrow_hashref ) {
- foreach my $field ( $record->field( $row->{fieldcode} ) ) {
- if ( ( $row->{subfieldcode} ne "" && $field->subfield( $row->{subfieldcode} ) ) ) {
- foreach my $subfield ( $field->subfield( $row->{subfieldcode} ) ) {
- push @result, { 'subfield' => $subfield };
- }
-
- } elsif ( $row->{subfieldcode} eq "" ) {
- push @result, { 'subfield' => $field->as_string() };
- }
- }
- }
-
- return \@result;
+ return C4::AuthoritiesMarc::CompareFieldWithAuthority( { 'field' => $field, 'authid' => $authid } );
}
=head2 GetBiblioData
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 $sysprefname = $template eq 'opac' ? 'opacisbd' : 'isbd';
my $framework = $params->{framework};
my $itemtype = $framework;
- my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch", $itemtype );
- my $tagslib = &GetMarcStructure( 1, $itemtype, { unsafe => 1 } );
+ my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch" );
+ 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
# Process subfield
}
-GetMarcStructure creates keys (lib, tab, mandatory, repeatable) for a display purpose.
+GetMarcStructure creates keys (lib, tab, mandatory, repeatable, important) for a display purpose.
These different values should not be processed as valid subfields.
=cut
my $dbh = C4::Context->dbh;
my $sth = $dbh->prepare(
- "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable
+ "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable,important,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, $mandatory, $repeatable, $important, $ind1_defaultvalue, $ind2_defaultvalue );
- while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) = $sth->fetchrow ) {
+ while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable, $important, $ind1_defaultvalue, $ind2_defaultvalue ) = $sth->fetchrow ) {
$res->{$tag}->{lib} = ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
$res->{$tag}->{tab} = "";
$res->{$tag}->{mandatory} = $mandatory;
+ $res->{$tag}->{important} = $important;
$res->{$tag}->{repeatable} = $repeatable;
+ $res->{$tag}->{ind1_defaultvalue} = $ind1_defaultvalue;
+ $res->{$tag}->{ind2_defaultvalue} = $ind2_defaultvalue;
}
- $sth = $dbh->prepare(
- "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
- "
- );
-
- $sth->execute($frameworkcode);
-
- my $subfield;
- my $authorised_value;
- my $authtypecode;
- my $value_builder;
- my $kohafield;
- my $seealso;
- my $hidden;
- 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,
- $maxlength
- )
- = $sth->fetchrow
- ) {
- $res->{$tag}->{$subfield}->{lib} = ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
- $res->{$tag}->{$subfield}->{tab} = $tab;
- $res->{$tag}->{$subfield}->{mandatory} = $mandatory;
- $res->{$tag}->{$subfield}->{repeatable} = $repeatable;
- $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
- $res->{$tag}->{$subfield}->{authtypecode} = $authtypecode;
- $res->{$tag}->{$subfield}->{value_builder} = $value_builder;
- $res->{$tag}->{$subfield}->{kohafield} = $kohafield;
- $res->{$tag}->{$subfield}->{seealso} = $seealso;
- $res->{$tag}->{$subfield}->{hidden} = $hidden;
- $res->{$tag}->{$subfield}->{isurl} = $isurl;
- $res->{$tag}->{$subfield}->{'link'} = $link;
- $res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue;
- $res->{$tag}->{$subfield}->{maxlength} = $maxlength;
+ my $mss = Koha::MarcSubfieldStructures->search( { frameworkcode => $frameworkcode } )->unblessed;
+ for my $m (@$mss) {
+ $res->{ $m->{tagfield} }->{ $m->{tagsubfield} } = {
+ lib => ( $forlibrarian or !$m->{libopac} ) ? $m->{liblibrarian} : $m->{libopac},
+ subfield => $m->{tagsubfield},
+ %$m
+ };
}
$cache->set_in_cache($cache_key, $res);
my $results = GetUsedMarcStructure($frameworkcode);
-C<$results> is a ref to an array which each case containts a ref
+C<$results> is a ref to an array which each case contains a ref
to a hash which each keys is the columns from marc_subfield_structure
C<$frameworkcode> is the framework code.
FROM marc_subfield_structure
WHERE tab > -1
AND frameworkcode = ?
- ORDER BY tagfield, tagsubfield
+ ORDER BY tagfield, display_order, tagsubfield
};
my $sth = C4::Context->dbh->prepare($query);
$sth->execute($frameworkcode);
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;
- my $subfield_structure = $dbh->selectall_hashref( q|
+ # We moved to selectall_arrayref since selectall_hashref does not
+ # keep duplicate mappings on kohafield (like place in 260 vs 264)
+ my $subfield_aref = $dbh->selectall_arrayref( q|
SELECT *
FROM marc_subfield_structure
WHERE frameworkcode = ?
AND kohafield > ''
- |, 'kohafield', {}, $frameworkcode );
-
+ ORDER BY frameworkcode, tagfield, display_order, tagsubfield
+ |, { Slice => {} }, $frameworkcode );
+ # Now map the output to a hash structure
+ my $subfield_structure = {};
+ foreach my $row ( @$subfield_aref ) {
+ push @{ $subfield_structure->{ $row->{kohafield} }}, $row;
+ }
$cache->set_in_cache( $cache_key, $subfield_structure );
return $subfield_structure;
}
=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 );
- return ( $mss->{$kohafield}{tagfield}, $mss->{$kohafield}{tagsubfield} );
+ 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 ? $retval[0] : undef );
}
=head2 GetMarcSubfieldStructureFromKohaField
- my $subfield_structure = &GetMarcSubfieldStructureFromKohaField($kohafield, $frameworkcode);
+ my $str = GetMarcSubfieldStructureFromKohaField( $kohafield );
-Returns a hashref where keys are marc_subfield_structure column names for the
-row where kohafield=$kohafield for the given framework code.
-
-$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 );
- return exists $mss->{$kohafield}
- ? $mss->{$kohafield}
- : undef;
+ # 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];
}
=head2 GetMarcBiblio
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';
if ($marcxml) {
$record = eval {
- MARC::Record::new_from_xml( $marcxml, "utf8",
+ MARC::Record::new_from_xml( $marcxml, "UTF-8",
C4::Context->preference('marcflavour') );
};
if ($@) { warn " problem with :$biblionumber : $@ \n$marcxml"; }
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;
}
-=head2 GetCOinSBiblio
-
- my $coins = GetCOinSBiblio($record);
-
-Returns the COinS (a span) which can be included in a biblio record
-
-=cut
-
-sub GetCOinSBiblio {
- my $record = shift;
-
- # get the coin format
- if ( ! $record ) {
- carp 'GetCOinSBiblio called with undefined record';
- return;
- }
- my $pos7 = substr $record->leader(), 7, 1;
- my $pos6 = substr $record->leader(), 6, 1;
- my $mtx;
- my $genre;
- my ( $aulast, $aufirst ) = ( '', '' );
- my $oauthors = '';
- my $title = '';
- my $subtitle = '';
- my $pubyear = '';
- my $isbn = '';
- my $issn = '';
- my $publisher = '';
- my $pages = '';
- my $titletype = 'b';
-
- # For the purposes of generating COinS metadata, LDR/06-07 can be
- # considered the same for UNIMARC and MARC21
- my $fmts6;
- my $fmts7;
- %$fmts6 = (
- 'a' => 'book',
- 'b' => 'manuscript',
- 'c' => 'book',
- 'd' => 'manuscript',
- 'e' => 'map',
- 'f' => 'map',
- 'g' => 'film',
- 'i' => 'audioRecording',
- 'j' => 'audioRecording',
- 'k' => 'artwork',
- 'l' => 'document',
- 'm' => 'computerProgram',
- 'o' => 'document',
- 'r' => 'document',
- );
- %$fmts7 = (
- 'a' => 'journalArticle',
- 's' => 'journal',
- );
-
- $genre = $fmts6->{$pos6} ? $fmts6->{$pos6} : 'book';
-
- if ( $genre eq 'book' ) {
- $genre = $fmts7->{$pos7} if $fmts7->{$pos7};
- }
-
- ##### We must transform mtx to a valable mtx and document type ####
- if ( $genre eq 'book' ) {
- $mtx = 'book';
- } elsif ( $genre eq 'journal' ) {
- $mtx = 'journal';
- $titletype = 'j';
- } elsif ( $genre eq 'journalArticle' ) {
- $mtx = 'journal';
- $genre = 'article';
- $titletype = 'a';
- } else {
- $mtx = 'dc';
- }
-
- $genre = ( $mtx eq 'dc' ) ? "&rft.type=$genre" : "&rft.genre=$genre";
-
- if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
-
- # Setting datas
- $aulast = $record->subfield( '700', 'a' ) || '';
- $aufirst = $record->subfield( '700', 'b' ) || '';
- $oauthors = "&rft.au=$aufirst $aulast";
-
- # others authors
- if ( $record->field('200') ) {
- for my $au ( $record->field('200')->subfield('g') ) {
- $oauthors .= "&rft.au=$au";
- }
- }
- $title =
- ( $mtx eq 'dc' )
- ? "&rft.title=" . $record->subfield( '200', 'a' )
- : "&rft.title=" . $record->subfield( '200', 'a' ) . "&rft.btitle=" . $record->subfield( '200', 'a' );
- $pubyear = $record->subfield( '210', 'd' ) || '';
- $publisher = $record->subfield( '210', 'c' ) || '';
- $isbn = $record->subfield( '010', 'a' ) || '';
- $issn = $record->subfield( '011', 'a' ) || '';
- } else {
-
- # MARC21 need some improve
-
- # Setting datas
- if ( $record->field('100') ) {
- $oauthors .= "&rft.au=" . $record->subfield( '100', 'a' );
- }
-
- # others authors
- if ( $record->field('700') ) {
- for my $au ( $record->field('700')->subfield('a') ) {
- $oauthors .= "&rft.au=$au";
- }
- }
- $title = "&rft." . $titletype . "title=" . $record->subfield( '245', 'a' );
- $subtitle = $record->subfield( '245', 'b' ) || '';
- $title .= $subtitle;
- if ($titletype eq 'a') {
- $pubyear = $record->field('008') || '';
- $pubyear = substr($pubyear->data(), 7, 4) if $pubyear;
- $isbn = $record->subfield( '773', 'z' ) || '';
- $issn = $record->subfield( '773', 'x' ) || '';
- if ($mtx eq 'journal') {
- $title .= "&rft.title=" . ( $record->subfield( '773', 't' ) || $record->subfield( '773', 'a') || q{} );
- } else {
- $title .= "&rft.btitle=" . ( $record->subfield( '773', 't' ) || $record->subfield( '773', 'a') || q{} );
- }
- foreach my $rel ($record->subfield( '773', 'g' )) {
- if ($pages) {
- $pages .= ', ';
- }
- $pages .= $rel;
- }
- } else {
- $pubyear = $record->subfield( '260', 'c' ) || '';
- $publisher = $record->subfield( '260', 'b' ) || '';
- $isbn = $record->subfield( '020', 'a' ) || '';
- $issn = $record->subfield( '022', 'a' ) || '';
- }
-
- }
- my $coins_value =
-"ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3A$mtx$genre$title&rft.isbn=$isbn&rft.issn=$issn&rft.aulast=$aulast&rft.aufirst=$aufirst$oauthors&rft.pub=$publisher&rft.date=$pubyear&rft.pages=$pages";
- $coins_value =~ s/(\ |&[^a])/\+/g;
- $coins_value =~ s/\"/\"\;/g;
-
-#<!-- TMPL_VAR NAME="ocoins_format" -->&rft.au=<!-- TMPL_VAR NAME="author" -->&rft.btitle=<!-- TMPL_VAR NAME="title" -->&rft.date=<!-- TMPL_VAR NAME="publicationyear" -->&rft.pages=<!-- TMPL_VAR NAME="pages" -->&rft.isbn=<!-- TMPL_VAR NAME=amazonisbn -->&rft.aucorp=&rft.place=<!-- TMPL_VAR NAME="place" -->&rft.pub=<!-- TMPL_VAR NAME="publishercode" -->&rft.edition=<!-- TMPL_VAR NAME="edition" -->&rft.series=<!-- TMPL_VAR NAME="series" -->&rft.genre="
-
- return $coins_value;
-}
-
-
=head2 GetMarcPrice
return the prices in accordance with the Marc format.
#---- 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
my @marcisbns;
foreach my $field ( $record->field($scope) ) {
my $isbn = $field->subfield( 'a' );
- if ( $isbn ne "" ) {
+ if ( $isbn && $isbn ne "" ) {
push @marcisbns, $isbn;
}
}
return \@marcissns;
} # end GetMarcISSN
-=head2 GetMarcNotes
-
- $marcnotesarray = GetMarcNotes( $record, $marcflavour );
-
- 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
-
-sub GetMarcNotes {
- my ( $record, $marcflavour ) = @_;
- if (!$record) {
- carp 'GetMarcNotes called on undefined record';
- return;
- }
-
- my $scope = $marcflavour eq "UNIMARC"? '3..': '5..';
- my @marcnotes;
- my %blacklist = map { $_ => 1 }
- split( /,/, C4::Context->preference('NotesBlacklist'));
- 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') };
- foreach my $sub ( $field->subfield('u') ) {
- push @marcnotes, { marcnote => $sub };
- }
- } else {
- push @marcnotes, { marcnote => $field->as_string() };
- }
- }
- return \@marcnotes;
-}
-
=head2 GetMarcSubjects
$marcsubjcts = GetMarcSubjects($record,$marcflavour);
return \@marcseries;
} #end getMARCseriess
-=head2 GetMarcHosts
-
- $marchostsarray = GetMarcHosts($record,$marcflavour);
-
-Get all host records (773s MARC21, 461 UNIMARC) from the MARC record and returns them in an array.
-
-=cut
-
-sub GetMarcHosts {
- my ( $record, $marcflavour ) = @_;
- if (!$record) {
- carp 'GetMarcHosts called on undefined record';
- return;
- }
-
- my ( $tag,$title_subf,$bibnumber_subf,$itemnumber_subf);
- $marcflavour ||="MARC21";
- if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
- $tag = "773";
- $title_subf = "t";
- $bibnumber_subf ="0";
- $itemnumber_subf='9';
- }
- elsif ($marcflavour eq "UNIMARC") {
- $tag = "461";
- $title_subf = "t";
- $bibnumber_subf ="0";
- $itemnumber_subf='9';
- };
-
- my @marchosts;
-
- foreach my $field ( $record->field($tag)) {
-
- my @fields_loop;
-
- my $hostbiblionumber = $field->subfield("$bibnumber_subf");
- my $hosttitle = $field->subfield($title_subf);
- my $hostitemnumber=$field->subfield($itemnumber_subf);
- push @fields_loop, { hostbiblionumber => $hostbiblionumber, hosttitle => $hosttitle, hostitemnumber => $hostitemnumber};
- push @marchosts, { MARCHOSTS_FIELDS_LOOP => \@fields_loop };
-
- }
- my $marchostsarray = \@marchosts;
- return $marchostsarray;
-}
-
=head2 UpsertMarcSubfield
my $record = C4::Biblio::UpsertMarcSubfield($MARC::Record, $fieldTag, $subfieldCode, $subfieldContent);
=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 = shift;
+ my ( $hash, $params ) = @_;
my $record = MARC::Record->new();
SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
- # FIXME Do not we want to get the marc subfield structure for the biblio framework?
- my $mss = GetMarcSubfieldStructure();
+
+ # 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 framework
my $tag_hr = {};
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->{$tagfield} //= [];
- push @{$tag_hr->{$tagfield}}, [($tagsubfield, $value)];
+ foreach my $fld ( @{ $mss->{$kohafield} } ) {
+ my $tagfield = $fld->{tagfield};
+ my $tagsubfield = $fld->{tagsubfield};
+ next if !$tagfield;
+
+ # BZ 21800: split value if field is repeatable.
+ my @values = _check_split($params, $fld, $value)
+ ? split(/\s?\|\s?/, $value, -1)
+ : ( $value );
+ foreach my $value ( @values ) {
+ next if $value eq '';
+ $tag_hr->{$tagfield} //= [];
+ push @{$tag_hr->{$tagfield}}, [($tagsubfield, $value)];
+ }
}
}
foreach my $tag (sort keys %$tag_hr) {
my @sfl = @{$tag_hr->{$tag}};
@sfl = sort { $a->[0] cmp $b->[0]; } @sfl;
@sfl = map { @{$_}; } @sfl;
- $record->insert_fields_ordered(
- MARC::Field->new($tag, " ", " ", @sfl)
- );
+ # Special care for control fields: remove the subfield indication @
+ # and do not insert indicators.
+ my @ind = $tag < 10 ? () : ( " ", " " );
+ @sfl = grep { $_ ne '@' } @sfl if $tag < 10;
+ $record->insert_fields_ordered( MARC::Field->new($tag, @ind, @sfl) );
}
return $record;
}
+sub _check_split {
+# Checks if $value must be split; may consult passed framework
+ my ($params, $fld, $value) = @_;
+ return if index($value,'|') == -1; # nothing to worry about
+ return if $params->{no_split};
+
+ # if we did not get a specific framework, check default in $mss
+ return $fld->{repeatable} if !$params->{framework};
+
+ # here we need to check the specific framework
+ my $mss = GetMarcSubfieldStructure($params->{framework}, { unsafe => 1 });
+ foreach my $fld2 ( @{ $mss->{ $fld->{kohafield} } } ) {
+ next if $fld2->{tagfield} ne $fld->{tagfield};
+ next if $fld2->{tagsubfield} ne $fld->{tagsubfield};
+ return 1 if $fld2->{repeatable};
+ }
+ return;
+}
+
=head2 PrepHostMarcField
$hostfield = PrepHostMarcField ( $hostbiblionumber,$hostitemnumber,$marcflavour )
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 ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
# NOTE: The parameter $ind_tag is NOT USED -- BZ 11247
+ my ( $perm_loc_tag, $perm_loc_subfield ) = C4::Biblio::GetMarcFromKohaField( "items.permanent_location" );
+
my $xml = MARC::File::XML::header('UTF-8');
$xml .= "<record>\n";
$auth_type = C4::Context->preference('marcflavour') unless $auth_type;
# MARC::Record->new_from_xml will fail (and Koha will die)
my $unimarc_and_100_exist = 0;
$unimarc_and_100_exist = 1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
- my $prevvalue;
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" ) {
# if we have a 100 field and it's values are not correct, skip them.
@$values[$i] =~ s/"/"/g;
@$values[$i] =~ s/'/'/g;
+ my $skip = @$values[$i] eq q{};
+ $skip = 0
+ if $perm_loc_tag
+ && $perm_loc_subfield
+ && @$tags[$i] eq $perm_loc_tag
+ && @$subfields[$i] eq $perm_loc_subfield;
+
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 ) };
- my $ind1 = _default_ind_to_space($indicator1);
- my $ind2;
- if ( @$indicator[$j] ) {
- $ind2 = _default_ind_to_space($indicator2);
- } else {
- warn "Indicator in @$tags[$i] is empty";
- $ind2 = " ";
- }
+ my $str = ( $indicator->[$j] // q{} ) . ' '; # extra space prevents substr outside of string warn
+ my $ind1 = _default_ind_to_space( substr( $str, 0, 1 ) );
+ my $ind2 = _default_ind_to_space( substr( $str, 1, 1 ) );
if ( !$first ) {
$xml .= "</datafield>\n";
if ( ( @$tags[$i] && @$tags[$i] > 10 )
- && ( @$values[$i] ne "" ) ) {
+ && ( !$skip ) ) {
$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;
}
} else {
- if ( @$values[$i] ne "" ) {
+ if ( !$skip ) {
# leader
if ( @$tags[$i] eq "000" ) {
$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 { # @$tags[$i] eq $prevtag
- my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) };
- my $indicator2 = eval { substr( @$indicator[$j], 1, 1 ) };
- my $ind1 = _default_ind_to_space($indicator1);
- my $ind2;
- if ( @$indicator[$j] ) {
- $ind2 = _default_ind_to_space($indicator2);
- } else {
- warn "Indicator in @$tags[$i] is empty";
- $ind2 = " ";
- }
- if ( @$values[$i] eq "" ) {
- } else {
+ if ( !$skip ) {
if ($first) {
+ my $str = ( $indicator->[$j] // q{} ) . ' '; # extra space prevents substr outside of string warn
+ my $ind1 = _default_ind_to_space( substr( $str, 0, 1 ) );
+ my $ind2 = _default_ind_to_space( substr( $str, 1, 1 ) );
$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 TransformHtmlToMarc
L<$record> = TransformHtmlToMarc(L<$cgi>)
- L<$cgi> is the CGI object which containts the values for subfields
+ L<$cgi> is the CGI object which contains the values for subfields
{
'tag_010_indicator1_531951' ,
'tag_010_indicator2_531951' ,
}
}
+ @fields = sort { $a->tag() cmp $b->tag() } @fields;
$record->append_fields(@fields);
return $record;
}
=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 = '' unless defined $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;
- }
-
- # 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};
- next ENTRY unless exists $tables{$table};
- my $key = _disambiguate( $table, $column );
- if ( $result->{$key} ) {
- unless ( ( $key eq "biblionumber" or $key eq "biblioitemnumber" ) and ( $field->data() eq "" ) ) {
- $result->{$key} .= " | " . $field->data();
- }
- } else {
- $result->{$key} = $field->data();
- }
- }
- } 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};
- my $key = _disambiguate( $table, $column );
- if ( $result->{$key} ) {
- unless ( ( $key eq "biblionumber" or $key eq "biblioitemnumber" ) and ( $value eq "" ) ) {
- $result->{$key} .= " | " . $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;
- }
+ my %tables = ( biblio => 1, biblioitems => 1, items => 1 );
+ if( $limit_table eq 'items' ) {
+ %tables = ( items => 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 } ) {
- 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 ];
+ 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 get_koha_field_from_marc
+=head2 TransformMarcToKohaOneField
- $result->{_disambiguate($table, $field)} =
- get_koha_field_from_marc($table,$field,$record,$frameworkcode);
+ $val = TransformMarcToKohaOneField( 'biblio.title', $marc );
-Internal function to map data from the MARC record to a specific non-MARC field.
-FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
+ Note: The authoritative Default framework is used implicitly.
=cut
-sub get_koha_field_from_marc {
- my ( $koha_table, $koha_column, $record, $frameworkcode ) = @_;
- my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table . '.' . $koha_column, $frameworkcode );
- my $kohafield;
- foreach my $field ( $record->field($tagfield) ) {
- if ( $field->tag() < 10 ) {
- if ($kohafield) {
- $kohafield .= " | " . $field->data();
+sub TransformMarcToKohaOneField {
+ 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 {
- $kohafield = $field->data();
- }
- } else {
- if ( $field->subfields ) {
- my @subfields = $field->subfields();
- foreach my $subfieldcount ( 0 .. $#subfields ) {
- if ( $subfields[$subfieldcount][0] eq $subfield ) {
- if ($kohafield) {
- $kohafield .= " | " . $subfields[$subfieldcount][1];
- } else {
- $kohafield = $subfields[$subfieldcount][1];
- }
- }
- }
+ push @rv, grep { $_ } $fld->subfield($sub);
}
}
}
- return $kohafield;
-}
-
-=head2 TransformMarcToKohaOneField
+ return unless @rv;
+ $retval = join ' | ', uniq(@rv);
- $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
-
-=cut
+ # Additional polishing for individual kohafields
+ if( $kohafield =~ /copyrightdate|publicationyear/ ) {
+ $retval = _adjust_pubyear( $retval );
+ }
-sub TransformMarcToKohaOneField {
+ return $retval;
+}
- # FIXME ? if a field has a repeatable subfield that is used in old-db,
- # only the 1st will be retrieved...
- my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
- my $res = "";
- my ( $tagfield, $subfield ) = GetMarcFromKohaField( $kohatable . "." . $kohafield, $frameworkcode );
- foreach my $field ( $record->field($tagfield) ) {
- if ( $field->tag() < 10 ) {
- if ( $result->{$kohafield} ) {
- $result->{$kohafield} .= " | " . $field->data();
- } else {
- $result->{$kohafield} = $field->data();
- }
- } else {
- if ( $field->subfields ) {
- my @subfields = $field->subfields();
- foreach my $subfieldcount ( 0 .. $#subfields ) {
- if ( $subfields[$subfieldcount][0] eq $subfield ) {
- if ( $result->{$kohafield} ) {
- $result->{$kohafield} .= " | " . $subfields[$subfieldcount][1];
- } else {
- $result->{$kohafield} = $subfields[$subfieldcount][1];
- }
- }
- }
- }
- }
+=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) ));
+ } else {
+ $retval = undef;
}
- return $result;
+ return $retval;
}
+=head2 CountItemsIssued
-#"
+ my $count = CountItemsIssued( $biblionumber );
-#
-# 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 misc/cronjobs/zebraqueue_start.pl script
-# =head2 ModZebrafiles
-#
-# &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
-#
-# =cut
-#
-# sub ModZebrafiles {
-#
-# my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
-#
-# my $op;
-# my $zebradir =
-# C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
-# unless ( opendir( DIR, "$zebradir" ) ) {
-# warn "$zebradir not found";
-# return;
-# }
-# closedir DIR;
-# my $filename = $zebradir . $biblionumber;
-#
-# if ($record) {
-# open( OUTPUT, ">", $filename . ".xml" );
-# print OUTPUT $record;
-# close OUTPUT;
-# }
-# }
+=cut
-=head2 ModZebra
+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'};
+}
- ModZebra( $biblionumber, $op, $server, $record );
+=head2 ModZebra
-$biblionumber is the biblionumber we want to index
+ ModZebra( $record_number, $op, $server );
-$op is specialUpdate or recordDelete, and is used to know what we want to do
+$record_number is the authid or biblionumber we want to index
-$server is the server that we want to update
+$op is the operation: specialUpdate or recordDelete
-$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.
+$server is authorityserver or biblioserver
=cut
sub ModZebra {
-###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
- 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::SearchEngine::Elasticsearch::Indexer;
- my $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new(
- {
- index => $server eq 'biblioserver'
- ? $Koha::SearchEngine::BIBLIOS_INDEX
- : $Koha::SearchEngine::AUTHORITIES_INDEX
- }
- );
- if ( $op eq 'specialUpdate' ) {
- unless ($record) {
- $record = GetMarcBiblio({
- biblionumber => $biblionumber,
- embed_items => 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 ( $record_number, $op, $server ) = @_;
+ Koha::Logger->get->debug("ModZebra: updates requested for: $record_number $op $server");
my $dbh = C4::Context->dbh;
# true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
AND operation = ?
AND done = 0";
my $check_sth = $dbh->prepare_cached($check_sql);
- $check_sth->execute( $server, $biblionumber, $op );
+ $check_sth->execute( $server, $record_number, $op );
my ($count) = $check_sth->fetchrow_array;
$check_sth->finish();
if ( $count == 0 ) {
my $sth = $dbh->prepare("INSERT INTO zebraqueue (biblio_auth_number,server,operation) VALUES(?,?,?)");
- $sth->execute( $biblionumber, $server, $op );
+ $sth->execute( $record_number, $server, $op );
$sth->finish;
}
}
-
=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 ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
+
+ 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;
sub _koha_marc_update_bib_ids {
my ( $record, $frameworkcode, $biblionumber, $biblioitemnumber ) = @_;
- my ( $biblio_tag, $biblio_subfield ) = GetMarcFromKohaField( "biblio.biblionumber", $frameworkcode );
+ my ( $biblio_tag, $biblio_subfield ) = GetMarcFromKohaField( "biblio.biblionumber" );
die qq{No biblionumber tag for framework "$frameworkcode"} unless $biblio_tag;
- my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber", $frameworkcode );
+ my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber" );
die qq{No biblioitemnumber tag for framework "$frameworkcode"} unless $biblioitem_tag;
if ( $biblio_tag < 10 ) {
my $biblioitem = shift;
my $frameworkcode = shift;
- my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.cn_sort", $frameworkcode );
+ my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.cn_sort" );
return unless $biblioitem_tag;
my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
}
}
-=head2 _koha_add_biblio
-
- my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
-
-Internal function to add a biblio ($biblio is a hash with the values)
-
-=cut
-
-sub _koha_add_biblio {
- my ( $dbh, $biblio, $frameworkcode ) = @_;
-
- my $error;
-
- # set the series flag
- unless (defined $biblio->{'serial'}){
- $biblio->{'serial'} = 0;
- if ( $biblio->{'seriestitle'} ) { $biblio->{'serial'} = 1 }
- }
-
- my $query = "INSERT INTO biblio
- SET frameworkcode = ?,
- author = ?,
- title = ?,
- unititle =?,
- notes = ?,
- serial = ?,
- seriestitle = ?,
- copyrightdate = ?,
- datecreated=NOW(),
- abstract = ?
- ";
- my $sth = $dbh->prepare($query);
- $sth->execute(
- $frameworkcode, $biblio->{'author'}, $biblio->{'title'}, $biblio->{'unititle'}, $biblio->{'notes'},
- $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}
- );
-
- my $biblionumber = $dbh->{'mysql_insertid'};
- if ( $dbh->errstr ) {
- $error .= "ERROR in _koha_add_biblio $query" . $dbh->errstr;
- warn $error;
- }
-
- $sth->finish();
-
- #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
- return ( $biblionumber, $error );
-}
-
=head2 _koha_modify_biblio
my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
SET frameworkcode = ?,
author = ?,
title = ?,
+ subtitle = ?,
+ medium = ?,
+ part_number = ?,
+ part_name = ?,
unititle = ?,
notes = ?,
serial = ?,
my $sth = $dbh->prepare($query);
$sth->execute(
- $frameworkcode, $biblio->{'author'}, $biblio->{'title'}, $biblio->{'unititle'}, $biblio->{'notes'},
- $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}, $biblio->{'biblionumber'}
+ $frameworkcode, $biblio->{'author'}, $biblio->{'title'}, $biblio->{'subtitle'},
+ $biblio->{'medium'}, $biblio->{'part_number'}, $biblio->{'part_name'}, $biblio->{'unititle'},
+ $biblio->{'notes'}, $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'} ? int($biblio->{'copyrightdate'}) : undef,
+ $biblio->{'abstract'}, $biblio->{'biblionumber'}
) if $biblio->{'biblionumber'};
if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
return ( $biblioitem->{'biblioitemnumber'}, $error );
}
-=head2 _koha_add_biblioitem
-
- my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
-
-Internal function to add a biblioitem
-
-=cut
-
-sub _koha_add_biblioitem {
- my ( $dbh, $biblioitem ) = @_;
- my $error;
-
- my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
- my $query = "INSERT INTO biblioitems SET
- biblionumber = ?,
- volume = ?,
- number = ?,
- itemtype = ?,
- isbn = ?,
- issn = ?,
- publicationyear = ?,
- publishercode = ?,
- volumedate = ?,
- volumedesc = ?,
- collectiontitle = ?,
- collectionissn = ?,
- collectionvolume= ?,
- editionstatement= ?,
- editionresponsibility = ?,
- illus = ?,
- pages = ?,
- notes = ?,
- size = ?,
- place = ?,
- lccn = ?,
- url = ?,
- cn_source = ?,
- cn_class = ?,
- cn_item = ?,
- cn_suffix = ?,
- cn_sort = ?,
- totalissues = ?,
- ean = ?,
- agerestriction = ?
- ";
- my $sth = $dbh->prepare($query);
- $sth->execute(
- $biblioitem->{'biblionumber'}, $biblioitem->{'volume'}, $biblioitem->{'number'}, $biblioitem->{'itemtype'},
- $biblioitem->{'isbn'}, $biblioitem->{'issn'}, $biblioitem->{'publicationyear'}, $biblioitem->{'publishercode'},
- $biblioitem->{'volumedate'}, $biblioitem->{'volumedesc'}, $biblioitem->{'collectiontitle'}, $biblioitem->{'collectionissn'},
- $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
- $biblioitem->{'pages'}, $biblioitem->{'bnotes'}, $biblioitem->{'size'}, $biblioitem->{'place'},
- $biblioitem->{'lccn'}, $biblioitem->{'url'}, $biblioitem->{'biblioitems.cn_source'},
- $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'}, $biblioitem->{'cn_suffix'}, $cn_sort,
- $biblioitem->{'totalissues'}, $biblioitem->{'ean'}, $biblioitem->{'agerestriction'}
- );
- my $bibitemnum = $dbh->{'mysql_insertid'};
-
- if ( $dbh->errstr ) {
- $error .= "ERROR in _koha_add_biblioitem $query" . $dbh->errstr;
- warn $error;
- }
- $sth->finish();
- return ( $bibitemnum, $error );
-}
-
=head2 _koha_delete_biblio
$error = _koha_delete_biblio($dbh,$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 );
=head2 ModBiblioMarc
- &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
+ &ModBiblioMarc($newrec,$biblionumber);
Add MARC XML data for a biblio to koha
sub ModBiblioMarc {
# pass the MARC::Record to this function, and it will create the records in
# the marcxml field
- my ( $record, $biblionumber, $frameworkcode ) = @_;
+ my ( $record, $biblionumber ) = @_;
if ( !$record ) {
carp 'ModBiblioMarc passed an undefined record';
return;
$record = $record->clone();
my $dbh = C4::Context->dbh;
my @fields = $record->fields();
- if ( !$frameworkcode ) {
- $frameworkcode = "";
- }
- my $sth = $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
- $sth->execute( $frameworkcode, $biblionumber );
- $sth->finish;
my $encoding = C4::Context->preference("marcflavour");
# deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
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;
- }
- ModZebra( $biblionumber, "specialUpdate", "biblioserver", $record );
- return $biblionumber;
-}
+ $record->as_usmarc; # Bug 20126/10455 This triggers field length calculation
-=head2 CountBiblioInOrders
+ my $m_rs = Koha::Biblio::Metadatas->find($metadata) //
+ Koha::Biblio::Metadata->new($metadata);
- $count = &CountBiblioInOrders( $biblionumber);
+ 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);
+ }
-This function return count of biblios in orders with $biblionumber
+ $m_rs->metadata( $record->as_xml_record($encoding) );
+ $m_rs->store;
-=cut
+ my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
+ $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
-sub CountBiblioInOrders {
- my ($biblionumber) = @_;
- my $dbh = C4::Context->dbh;
- my $query = "SELECT count(*)
- FROM aqorders
- WHERE biblionumber=? AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')";
- my $sth = $dbh->prepare($query);
- $sth->execute($biblionumber);
- my $count = $sth->fetchrow;
- return ($count);
+ return $biblionumber;
}
=head2 prepare_host_field
return;
}
my $biblioitem = $biblio->biblioitem;
- my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField('biblioitems.totalissues', $biblio->frameworkcode);
+ my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField( 'biblioitems.totalissues' );
unless ($totalissuestag) {
return 1; # There is nothing to do
}
1;
+=head2 _after_biblio_action_hooks
+
+Helper method that takes care of calling all plugin hooks
+
+=cut
+
+sub _after_biblio_action_hooks {
+ my ( $args ) = @_;
+
+ my $biblio_id = $args->{biblio_id};
+ my $action = $args->{action};
+
+ my $biblio = Koha::Biblios->find( $biblio_id );
+ Koha::Plugins->call(
+ 'after_biblio_action',
+ {
+ action => $action,
+ biblio => $biblio,
+ biblio_id => $biblio_id,
+ }
+ );
+}
+
__END__
=head1 AUTHOR