use C4::Dates qw/format_date/;
use C4::Log; # logaction
use C4::ClassSource;
+use C4::Charset;
use vars qw($VERSION @ISA @EXPORT);
@ISA = qw( Exporter );
# to add biblios
+# EXPORTED FUNCTIONS.
push @EXPORT, qw(
&AddBiblio
);
&GetMarcSeries
GetMarcUrls
&GetUsedMarcStructure
-
&GetXmlBiblio
&GetAuthorisedValueDesc
&ModBiblioframework
&ModZebra
);
-
# To delete something
push @EXPORT, qw(
&DelBiblio
);
+ # To link headings in a bib record
+ # to authority records.
+ push @EXPORT, qw(
+ &LinkBibHeadingsToAuthorities
+ );
+
# Internal functions
# those functions are exported but should not be used
# they are usefull is few circumstances, so are exported.
push @EXPORT, qw(
&ModBiblioMarc
);
-
# Others functions
push @EXPORT, qw(
&TransformMarcToKoha
&TransformHtmlToMarc
&TransformHtmlToXml
&PrepareItemrecordDisplay
- &char_decode
&GetNoZebraIndexes
);
}
+# because of interdependencies between
+# C4::Search, C4::Heading, and C4::Biblio,
+# 'use C4::Heading' must occur after
+# the exports have been defined.
+use C4::Heading;
+
=head1 NAME
C4::Biblio - cataloging management functions
_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);
+
# now add the record
$biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode ) unless $defer_marc_save;
$sth->finish();
_koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
- # update the MARC record (that now contains biblio and items) with the new record data
- &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
-
# load the koha-table data object
my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
+ # update MARC subfield that stores biblioitems.cn_sort
+ _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 );
+
# modify the other koha tables
_koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
_koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
return;
}
+=head2 LinkBibHeadingsToAuthorities
+
+=over 4
+
+my $headings_linked = LinkBibHeadingsToAuthorities($marc);
+
+=back
+
+Links bib headings to authority records by checking
+each authority-controlled field in the C<MARC::Record>
+object C<$marc>, looking for a matching authority record,
+and setting the linking subfield $9 to the ID of that
+authority record.
+
+If no matching authority exists, or if multiple
+authorities match, no $9 will be added, and any
+existing one inthe field will be deleted.
+
+Returns the number of heading links changed in the
+MARC record.
+
+=cut
+
+sub LinkBibHeadingsToAuthorities {
+ my $bib = shift;
+
+ my $num_headings_changed = 0;
+ foreach my $field ($bib->fields()) {
+ my $heading = C4::Heading->new_from_bib_field($field);
+ next unless defined $heading;
+
+ # check existing $9
+ my $current_link = $field->subfield('9');
+
+ # look for matching authorities
+ my $authorities = $heading->authorities();
+
+ # want only one exact match
+ if ($#{ $authorities } == 0) {
+ my $authority = MARC::Record->new_from_usmarc($authorities->[0]);
+ my $authid = $authority->field('001')->data();
+ next if defined $current_link and $current_link eq $authid;
+
+ $field->delete_subfield(code => '9') if defined $current_link;
+ $field->add_subfields('9', $authid);
+ $num_headings_changed++;
+ } else {
+ if (defined $current_link) {
+ $field->delete_subfield(code => '9');
+ $num_headings_changed++;
+ }
+ }
+
+ }
+ return $num_headings_changed;
+}
+
=head2 GetBiblioData
=over 4
=cut
+# cache for results of GetMarcStructure -- needed
+# for batch jobs
+our $marc_structure_cache;
+
sub GetMarcStructure {
my ( $forlibrarian, $frameworkcode ) = @_;
my $dbh=C4::Context->dbh;
$frameworkcode = "" unless $frameworkcode;
+
+ if (defined $marc_structure_cache and exists $marc_structure_cache->{$forlibrarian}->{$frameworkcode}) {
+ return $marc_structure_cache->{$forlibrarian}->{$frameworkcode};
+ }
+
my $sth;
my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';
$res->{$tag}->{$subfield}->{'link'} = $link;
$res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue;
}
+
+ $marc_structure_cache->{$forlibrarian}->{$frameworkcode} = $res;
+
return $res;
}
my $sth =
$dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
$sth->execute($biblionumber);
- my ($marcxml) = $sth->fetchrow;
+ my $row = $sth->fetchrow_hashref;
+ my $marcxml = StripNonXmlChars($row->{'marcxml'});
MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
- $marcxml =~ s/\x1e//g;
- $marcxml =~ s/\x1f//g;
- $marcxml =~ s/\x1d//g;
- $marcxml =~ s/\x0f//g;
- $marcxml =~ s/\x0c//g;
-# warn $marcxml;
my $record = MARC::Record->new();
if ($marcxml) {
$record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
- if ($@) {warn $@;}
+ if ($@) {warn " problem with :$biblionumber : $@ \n$marcxml";}
# $record = MARC::Record::new_from_usmarc( $marc) if $marc;
return $record;
} else {
}
else {
# reset $linkvalue if UNIMARC author responsibility
- if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq '4')) {
+ if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq "4")) {
$linkvalue = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
}
push @link_loop, {'limit' => 'au', link => $linkvalue, operator => $operator };
}
+ $value = GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ) if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~/4/));
my @this_link_loop = @link_loop;
my $separator = C4::Context->preference("authoritysep") unless $count_auth==0;
push @subfields_loop, {code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($authors_subfield->[0] == 9 );
=head1 OTHER FUNCTIONS
-=head2 char_decode
-
-=over 4
-
-my $string = char_decode( $string, $encoding );
-
-converts ISO 5426 coded string to UTF-8
-sloppy code : should be improved in next issue
-
-=back
-
-=cut
-
-sub char_decode {
- my ( $string, $encoding ) = @_;
- $_ = $string;
-
- $encoding = C4::Context->preference("marcflavour") unless $encoding;
- if ( $encoding eq "UNIMARC" ) {
-
- # s/\xe1/Æ/gm;
- s/\xe2/Ğ/gm;
- s/\xe9/Ø/gm;
- s/\xec/ş/gm;
- s/\xf1/æ/gm;
- s/\xf3/ğ/gm;
- s/\xf9/ø/gm;
- s/\xfb/ß/gm;
- s/\xc1\x61/à/gm;
- s/\xc1\x65/è/gm;
- s/\xc1\x69/ì/gm;
- s/\xc1\x6f/ò/gm;
- s/\xc1\x75/ù/gm;
- s/\xc1\x41/À/gm;
- s/\xc1\x45/È/gm;
- s/\xc1\x49/Ì/gm;
- s/\xc1\x4f/Ò/gm;
- s/\xc1\x55/Ù/gm;
- s/\xc2\x41/Á/gm;
- s/\xc2\x45/É/gm;
- s/\xc2\x49/Í/gm;
- s/\xc2\x4f/Ó/gm;
- s/\xc2\x55/Ú/gm;
- s/\xc2\x59/İ/gm;
- s/\xc2\x61/á/gm;
- s/\xc2\x65/é/gm;
- s/\xc2\x69/í/gm;
- s/\xc2\x6f/ó/gm;
- s/\xc2\x75/ú/gm;
- s/\xc2\x79/ı/gm;
- s/\xc3\x41/Â/gm;
- s/\xc3\x45/Ê/gm;
- s/\xc3\x49/Î/gm;
- s/\xc3\x4f/Ô/gm;
- s/\xc3\x55/Û/gm;
- s/\xc3\x61/â/gm;
- s/\xc3\x65/ê/gm;
- s/\xc3\x69/î/gm;
- s/\xc3\x6f/ô/gm;
- s/\xc3\x75/û/gm;
- s/\xc4\x41/Ã/gm;
- s/\xc4\x4e/Ñ/gm;
- s/\xc4\x4f/Õ/gm;
- s/\xc4\x61/ã/gm;
- s/\xc4\x6e/ñ/gm;
- s/\xc4\x6f/õ/gm;
- s/\xc8\x41/Ä/gm;
- s/\xc8\x45/Ë/gm;
- s/\xc8\x49/Ï/gm;
- s/\xc8\x61/ä/gm;
- s/\xc8\x65/ë/gm;
- s/\xc8\x69/ï/gm;
- s/\xc8\x6F/ö/gm;
- s/\xc8\x75/ü/gm;
- s/\xc8\x76/ÿ/gm;
- s/\xc9\x41/Ä/gm;
- s/\xc9\x45/Ë/gm;
- s/\xc9\x49/Ï/gm;
- s/\xc9\x4f/Ö/gm;
- s/\xc9\x55/Ü/gm;
- s/\xc9\x61/ä/gm;
- s/\xc9\x6f/ö/gm;
- s/\xc9\x75/ü/gm;
- s/\xca\x41/Å/gm;
- s/\xca\x61/å/gm;
- s/\xd0\x43/Ç/gm;
- s/\xd0\x63/ç/gm;
-
- # this handles non-sorting blocks (if implementation requires this)
- $string = nsb_clean($_);
- }
- elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
- ##MARC-8 to UTF-8
-
- s/\xe1\x61/à/gm;
- s/\xe1\x65/è/gm;
- s/\xe1\x69/ì/gm;
- s/\xe1\x6f/ò/gm;
- s/\xe1\x75/ù/gm;
- s/\xe1\x41/À/gm;
- s/\xe1\x45/È/gm;
- s/\xe1\x49/Ì/gm;
- s/\xe1\x4f/Ò/gm;
- s/\xe1\x55/Ù/gm;
- s/\xe2\x41/Á/gm;
- s/\xe2\x45/É/gm;
- s/\xe2\x49/Í/gm;
- s/\xe2\x4f/Ó/gm;
- s/\xe2\x55/Ú/gm;
- s/\xe2\x59/İ/gm;
- s/\xe2\x61/á/gm;
- s/\xe2\x65/é/gm;
- s/\xe2\x69/í/gm;
- s/\xe2\x6f/ó/gm;
- s/\xe2\x75/ú/gm;
- s/\xe2\x79/ı/gm;
- s/\xe3\x41/Â/gm;
- s/\xe3\x45/Ê/gm;
- s/\xe3\x49/Î/gm;
- s/\xe3\x4f/Ô/gm;
- s/\xe3\x55/Û/gm;
- s/\xe3\x61/â/gm;
- s/\xe3\x65/ê/gm;
- s/\xe3\x69/î/gm;
- s/\xe3\x6f/ô/gm;
- s/\xe3\x75/û/gm;
- s/\xe4\x41/Ã/gm;
- s/\xe4\x4e/Ñ/gm;
- s/\xe4\x4f/Õ/gm;
- s/\xe4\x61/ã/gm;
- s/\xe4\x6e/ñ/gm;
- s/\xe4\x6f/õ/gm;
- s/\xe6\x41/Ă/gm;
- s/\xe6\x45/Ĕ/gm;
- s/\xe6\x65/ĕ/gm;
- s/\xe6\x61/ă/gm;
- s/\xe8\x45/Ë/gm;
- s/\xe8\x49/Ï/gm;
- s/\xe8\x65/ë/gm;
- s/\xe8\x69/ï/gm;
- s/\xe8\x76/ÿ/gm;
- s/\xe9\x41/A/gm;
- s/\xe9\x4f/O/gm;
- s/\xe9\x55/U/gm;
- s/\xe9\x61/a/gm;
- s/\xe9\x6f/o/gm;
- s/\xe9\x75/u/gm;
- s/\xea\x41/A/gm;
- s/\xea\x61/a/gm;
-
- #Additional Turkish characters
- s/\x1b//gm;
- s/\x1e//gm;
- s/(\xf0)s/\xc5\x9f/gm;
- s/(\xf0)S/\xc5\x9e/gm;
- s/(\xf0)c/ç/gm;
- s/(\xf0)C/Ç/gm;
- s/\xe7\x49/\\xc4\xb0/gm;
- s/(\xe6)G/\xc4\x9e/gm;
- s/(\xe6)g/ğ\xc4\x9f/gm;
- s/\xB8/ı/gm;
- s/\xB9/£/gm;
- s/(\xe8|\xc8)o/ö/gm;
- s/(\xe8|\xc8)O/Ö/gm;
- s/(\xe8|\xc8)u/ü/gm;
- s/(\xe8|\xc8)U/Ü/gm;
- s/\xc2\xb8/\xc4\xb1/gm;
- s/¸/\xc4\xb1/gm;
-
- # this handles non-sorting blocks (if implementation requires this)
- $string = nsb_clean($_);
- }
- return ($string);
-}
-
-=head2 nsb_clean
-
-=over 4
-
-my $string = nsb_clean( $string, $encoding );
-
-=back
-
-=cut
-
-sub nsb_clean {
- my $NSB = '\x88'; # NSB : begin Non Sorting Block
- my $NSE = '\x89'; # NSE : Non Sorting Block end
- # handles non sorting blocks
- my ($string) = @_;
- $_ = $string;
- s/$NSB/(/gm;
- s/[ ]{0,1}$NSE/) /gm;
- $string = $_;
- return ($string);
-}
=head2 PrepareItemrecordDisplay
#
# we use zebra, just fill zebraqueue table
#
- my $sth=$dbh->prepare("INSERT INTO zebraqueue (biblio_auth_number,server,operation) VALUES(?,?,?)");
- $sth->execute($biblionumber,$server,$op);
- $sth->finish;
+ my $check_sql = "SELECT COUNT(*) FROM zebraqueue
+ WHERE server = ?
+ AND biblio_auth_number = ?
+ AND operation = ?
+ AND done = 0";
+ my $check_sth = $dbh->prepare_cached($check_sql);
+ $check_sth->execute($server, $biblionumber, $op);
+ my ($count) = $check_sth->fetchrow_array;
+ $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->finish;
+ }
}
}
}
# remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
- $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
+ $title =~ s/ |\.|,|;|\[|\]|\(|\)|\*|-|'|:|=|\r|\n//g;
# limit to 10 char, should be enough, and limit the DB size
$title = substr($title,0,10);
#parse each field
my $tag = $field->tag();
my $subfieldcode = $subfield->[0];
my $indexed=0;
+ warn "INDEXING :".$subfield->[1];
# check each index to see if the subfield is stored somewhere
# otherwise, store it in __RAW__ index
foreach my $key (keys %index) {
$indexed=1;
my $line= lc $subfield->[1];
# remove meaningless value in the field...
- $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
+ $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
# ... and split in words
foreach (split / /,$line) {
next unless $_; # skip empty values (multiple spaces)
# if the entry is already here, improve weight
# warn "managing $_";
- if ($result{$key}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
+ if ($result{$key}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d);/) {
my $weight=$1+1;
- $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+ $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d);//;
$result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
} else {
# get the value if it exist in the nozebra table, otherwise, create it
if ($existing_biblionumbers) {
$result{$key}->{"$_"} =$existing_biblionumbers;
my $weight=$1+1;
- $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+ $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d);//;
$result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
# create a new ligne for this entry
} else {
# the subfield is not indexed, store it in __RAW__ index anyway
unless ($indexed) {
my $line= lc $subfield->[1];
- $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
+ $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
# ... and split in words
foreach (split / /,$line) {
next unless $_; # skip empty values (multiple spaces)
# if the entry is already here, improve weight
- if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
+ if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d);/) {
my $weight=$1+1;
- $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+ $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d);//;
$result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
} else {
# get the value if it exist in the nozebra table, otherwise, create it
if ($existing_biblionumbers) {
$result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
my $weight=$1+1;
- $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+ $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d);//;
$result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
# create a new ligne for this entry
} else {
}
}
+=head2 _koha_marc_update_biblioitem_cn_sort
+
+=over 4
+
+_koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
+
+=back
+
+Given a MARC bib record and the biblioitem hash, update the
+subfield that contains a copy of the value of biblioitems.cn_sort.
+
+=cut
+
+sub _koha_marc_update_biblioitem_cn_sort {
+ my $marc = shift;
+ my $biblioitem = shift;
+ my $frameworkcode= shift;
+
+ my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.cn_sort",$frameworkcode);
+ return unless $biblioitem_tag;
+
+ my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
+
+ if (my $field = $marc->field($biblioitem_tag)) {
+ $field->delete_subfield(code => $biblioitem_subfield);
+ if ($cn_sort ne '') {
+ $field->add_subfields($biblioitem_subfield => $cn_sort);
+ }
+ } else {
+ # if we get here, no biblioitem tag is present in the MARC record, so
+ # we'll create it if $cn_sort is not empty -- this would be
+ # an odd combination of events, however
+ if ($cn_sort) {
+ $marc->insert_grouped_field(MARC::Field->new($biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort));
+ }
+ }
+}
+
=head2 _koha_add_biblio
=over 4