3 # Copyright 2000-2002 Katipo Communications
4 # Copyright 2010 BibLibre
5 # Copyright 2011 Equinox Software, Inc.
7 # This file is part of Koha.
9 # Koha is free software; you can redistribute it and/or modify it
10 # under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # Koha is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with Koha; if not, see <http://www.gnu.org/licenses>.
24 use vars qw(@ISA @EXPORT);
47 GetAuthorisedValueDesc
49 IsMarcStructureInternal
51 GetMarcSubfieldStructureFromKohaField
62 LinkBibHeadingsToAuthorities
70 # those functions are exported but should not be used
71 # they are useful in a few circumstances, so they are exported,
72 # but don't use them unless you are a core developer ;-)
81 use Encode qw( decode is_utf8 );
82 use List::MoreUtils qw( uniq );
84 use MARC::File::USMARC;
86 use POSIX qw(strftime);
87 use Module::Load::Conditional qw(can_load);
90 use C4::Log; # logaction
99 use Koha::Authority::Types;
100 use Koha::Acquisition::Currencies;
101 use Koha::Biblio::Metadatas;
105 use Koha::SearchEngine;
106 use Koha::SearchEngine::Indexer;
108 use Koha::Util::MARC;
110 use vars qw($debug $cgi_debug);
115 C4::Biblio - cataloging management functions
119 Biblio.pm contains functions for managing storage and editing of bibliographic data within Koha. Most of the functions in this module are used for cataloging records: adding, editing, or removing biblios, biblioitems, or items. Koha's stores bibliographic information in three places:
123 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
125 =item 2. as raw MARC in the Zebra index and storage engine
127 =item 3. as MARC XML in biblio_metadata.metadata
131 In the 3.0 version of Koha, the authoritative record-level information is in biblio_metadata.metadata
133 Because the data isn't completely normalized there's a chance for information to get out of sync. The design choice to go with a un-normalized schema was driven by performance and stability concerns. However, if this occur, it can be considered as a bug : The API is (or should be) complete & the only entry point for all biblio/items managements.
137 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
139 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
143 Because of this design choice, the process of managing storage and editing is a bit convoluted. Historically, Biblio.pm's grown to an unmanagable size and as a result we have several types of functions currently:
147 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
149 =item 2. _koha_* - low-level internal functions for managing the koha tables
151 =item 3. Marc management function : as the MARC record is stored in biblio_metadata.metadata, some subs dedicated to it's management are in this package. They should be used only internally by Biblio.pm, the only official entry points being AddBiblio, AddItem, ModBiblio, ModItem.
153 =item 4. Zebra functions used to update the Zebra index
155 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
159 The MARC record (in biblio_metadata.metadata) contains the complete marc record, including items. It also contains the biblionumber. That is the reason why it is not stored directly by AddBiblio, with all other fields . To save a biblio, we need to :
163 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
165 =item 2. add the biblionumber and biblioitemnumber into the MARC records
167 =item 3. save the marc record
171 =head1 EXPORTED FUNCTIONS
175 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
177 Exported function (core API) for adding a new biblio to koha.
179 The first argument is a C<MARC::Record> object containing the
180 bib to add, while the second argument is the desired MARC
183 This function also accepts a third, optional argument: a hashref
184 to additional options. The only defined option is C<defer_marc_save>,
185 which if present and mapped to a true value, causes C<AddBiblio>
186 to omit the call to save the MARC in C<biblio_metadata.metadata>
187 This option is provided B<only>
188 for the use of scripts such as C<bulkmarcimport.pl> that may need
189 to do some manipulation of the MARC record for item parsing before
190 saving it and which cannot afford the performance hit of saving
191 the MARC record twice. Consequently, do not use that option
192 unless you can guarantee that C<ModBiblioMarc> will be called.
198 my $frameworkcode = shift;
199 my $options = @_ ? shift : undef;
200 my $defer_marc_save = 0;
202 carp('AddBiblio called with undefined record');
205 if ( defined $options and exists $options->{'defer_marc_save'} and $options->{'defer_marc_save'} ) {
206 $defer_marc_save = 1;
209 if (C4::Context->preference('BiblioAddsAuthorities')) {
210 BiblioAutoLink( $record, $frameworkcode );
213 my ( $biblionumber, $biblioitemnumber, $error );
214 my $dbh = C4::Context->dbh;
216 # transform the data into koha-table style data
217 SetUTF8Flag($record);
218 my $olddata = TransformMarcToKoha( $record, $frameworkcode );
219 my $schema = Koha::Database->schema;
221 $schema->txn_do(sub {
223 my $biblio = Koha::Biblio->new(
225 frameworkcode => $frameworkcode,
226 author => $olddata->{author},
227 title => $olddata->{title},
228 subtitle => $olddata->{subtitle},
229 medium => $olddata->{medium},
230 part_number => $olddata->{part_number},
231 part_name => $olddata->{part_name},
232 unititle => $olddata->{unititle},
233 notes => $olddata->{notes},
235 ( $olddata->{serial} || $olddata->{seriestitle} ? 1 : 0 ),
236 seriestitle => $olddata->{seriestitle},
237 copyrightdate => $olddata->{copyrightdate},
238 datecreated => \'NOW()',
239 abstract => $olddata->{abstract},
242 $biblionumber = $biblio->biblionumber;
244 my ($cn_sort) = GetClassSort( $olddata->{'biblioitems.cn_source'}, $olddata->{'cn_class'}, $olddata->{'cn_item'} );
245 my $biblioitem = Koha::Biblioitem->new(
247 biblionumber => $biblionumber,
248 volume => $olddata->{volume},
249 number => $olddata->{number},
250 itemtype => $olddata->{itemtype},
251 isbn => $olddata->{isbn},
252 issn => $olddata->{issn},
253 publicationyear => $olddata->{publicationyear},
254 publishercode => $olddata->{publishercode},
255 volumedate => $olddata->{volumedate},
256 volumedesc => $olddata->{volumedesc},
257 collectiontitle => $olddata->{collectiontitle},
258 collectionissn => $olddata->{collectionissn},
259 collectionvolume => $olddata->{collectionvolume},
260 editionstatement => $olddata->{editionstatement},
261 editionresponsibility => $olddata->{editionresponsibility},
262 illus => $olddata->{illus},
263 pages => $olddata->{pages},
264 notes => $olddata->{bnotes},
265 size => $olddata->{size},
266 place => $olddata->{place},
267 lccn => $olddata->{lccn},
268 url => $olddata->{url},
269 cn_source => $olddata->{'biblioitems.cn_source'},
270 cn_class => $olddata->{cn_class},
271 cn_item => $olddata->{cn_item},
272 cn_suffix => $olddata->{cn_suff},
274 totalissues => $olddata->{totalissues},
275 ean => $olddata->{ean},
276 agerestriction => $olddata->{agerestriction},
279 $biblioitemnumber = $biblioitem->biblioitemnumber;
281 _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber );
283 # update MARC subfield that stores biblioitems.cn_sort
284 _koha_marc_update_biblioitem_cn_sort( $record, $olddata, $frameworkcode );
287 ModBiblioMarc( $record, $biblionumber, $frameworkcode ) unless $defer_marc_save;
289 # update OAI-PMH sets
290 if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
291 C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
294 _after_biblio_action_hooks({ action => 'create', biblio_id => $biblionumber });
296 logaction( "CATALOGUING", "ADD", $biblionumber, "biblio" ) if C4::Context->preference("CataloguingLog");
300 ( $biblionumber, $biblioitemnumber ) = ( undef, undef );
302 return ( $biblionumber, $biblioitemnumber );
307 ModBiblio( $record,$biblionumber,$frameworkcode, $disable_autolink);
309 Replace an existing bib record identified by C<$biblionumber>
310 with one supplied by the MARC::Record object C<$record>. The embedded
311 item, biblioitem, and biblionumber fields from the previous
312 version of the bib record replace any such fields of those tags that
313 are present in C<$record>. Consequently, ModBiblio() is not
314 to be used to try to modify item records.
316 C<$frameworkcode> specifies the MARC framework to use
317 when storing the modified bib record; among other things,
318 this controls how MARC fields get mapped to display columns
319 in the C<biblio> and C<biblioitems> tables, as well as
320 which fields are used to store embedded item, biblioitem,
321 and biblionumber data for indexing.
323 Unless C<$disable_autolink> is passed ModBiblio will relink record headings
324 to authorities based on settings in the system preferences. This flag allows
325 us to not relink records when the authority linker is saving modifications.
327 Returns 1 on success 0 on failure
332 my ( $record, $biblionumber, $frameworkcode, $disable_autolink ) = @_;
334 carp 'No record passed to ModBiblio';
338 if ( C4::Context->preference("CataloguingLog") ) {
339 my $newrecord = GetMarcBiblio({ biblionumber => $biblionumber });
340 logaction( "CATALOGUING", "MODIFY", $biblionumber, "biblio BEFORE=>" . $newrecord->as_formatted );
343 if ( !$disable_autolink && C4::Context->preference('BiblioAddsAuthorities') ) {
344 BiblioAutoLink( $record, $frameworkcode );
347 # Cleaning up invalid fields must be done early or SetUTF8Flag is liable to
348 # throw an exception which probably won't be handled.
349 foreach my $field ($record->fields()) {
350 if (! $field->is_control_field()) {
351 if (scalar($field->subfields()) == 0 || (scalar($field->subfields()) == 1 && $field->subfield('9'))) {
352 $record->delete_field($field);
357 SetUTF8Flag($record);
358 my $dbh = C4::Context->dbh;
360 $frameworkcode = "" if !$frameworkcode || $frameworkcode eq "Default"; # XXX
362 _strip_item_fields($record, $frameworkcode);
364 # update biblionumber and biblioitemnumber in MARC
365 # FIXME - this is assuming a 1 to 1 relationship between
366 # biblios and biblioitems
367 my $sth = $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
368 $sth->execute($biblionumber);
369 my ($biblioitemnumber) = $sth->fetchrow;
371 _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber );
373 # load the koha-table data object
374 my $oldbiblio = TransformMarcToKoha( $record, $frameworkcode );
376 # update MARC subfield that stores biblioitems.cn_sort
377 _koha_marc_update_biblioitem_cn_sort( $record, $oldbiblio, $frameworkcode );
379 # update the MARC record (that now contains biblio and items) with the new record data
380 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
382 # modify the other koha tables
383 _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
384 _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
386 _after_biblio_action_hooks({ action => 'modify', biblio_id => $biblionumber });
388 # update OAI-PMH sets
389 if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
390 C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
396 =head2 _strip_item_fields
398 _strip_item_fields($record, $frameworkcode)
400 Utility routine to remove item tags from a
405 sub _strip_item_fields {
407 my $frameworkcode = shift;
408 # get the items before and append them to the biblio before updating the record, atm we just have the biblio
409 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
411 # delete any item fields from incoming record to avoid
412 # duplication or incorrect data - use AddItem() or ModItem()
414 foreach my $field ( $record->field($itemtag) ) {
415 $record->delete_field($field);
421 my $error = &DelBiblio($biblionumber);
423 Exported function (core API) for deleting a biblio in koha.
424 Deletes biblio record from Zebra and Koha tables (biblio & biblioitems)
425 Also backs it up to deleted* tables.
426 Checks to make sure that the biblio has no items attached.
428 C<$error> : undef unless an error occurs
433 my ($biblionumber, $params) = @_;
435 my $biblio = Koha::Biblios->find( $biblionumber );
436 return unless $biblio; # Should we throw an exception instead?
438 my $dbh = C4::Context->dbh;
439 my $error; # for error handling
441 # First make sure this biblio has no items attached
442 my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
443 $sth->execute($biblionumber);
444 if ( my $itemnumber = $sth->fetchrow ) {
446 # Fix this to use a status the template can understand
447 $error .= "This Biblio has items attached, please delete them first before deleting this biblio ";
450 return $error if $error;
452 # We delete any existing holds
453 my $holds = $biblio->holds;
454 while ( my $hold = $holds->next ) {
458 unless ( $params->{skip_record_index} ){
459 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
460 $indexer->index_records( $biblionumber, "recordDelete", "biblioserver" );
463 # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
464 $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
465 $sth->execute($biblionumber);
466 while ( my $biblioitemnumber = $sth->fetchrow ) {
468 # delete this biblioitem
469 $error = _koha_delete_biblioitems( $dbh, $biblioitemnumber );
470 return $error if $error;
474 # delete biblio from Koha tables and save in deletedbiblio
475 # must do this *after* _koha_delete_biblioitems, otherwise
476 # delete cascade will prevent deletedbiblioitems rows
477 # from being generated by _koha_delete_biblioitems
478 $error = _koha_delete_biblio( $dbh, $biblionumber );
480 _after_biblio_action_hooks({ action => 'delete', biblio_id => $biblionumber });
482 logaction( "CATALOGUING", "DELETE", $biblionumber, "biblio" ) if C4::Context->preference("CataloguingLog");
488 =head2 BiblioAutoLink
490 my $headings_linked = BiblioAutoLink($record, $frameworkcode)
492 Automatically links headings in a bib record to authorities.
494 Returns the number of headings changed
500 my $frameworkcode = shift;
502 carp('Undefined record passed to BiblioAutoLink');
505 my ( $num_headings_changed, %results );
508 "C4::Linker::" . ( C4::Context->preference("LinkerModule") || 'Default' );
509 unless ( can_load( modules => { $linker_module => undef } ) ) {
510 $linker_module = 'C4::Linker::Default';
511 unless ( can_load( modules => { $linker_module => undef } ) ) {
516 my $linker = $linker_module->new(
517 { 'options' => C4::Context->preference("LinkerOptions") } );
518 my ( $headings_changed, undef ) =
519 LinkBibHeadingsToAuthorities( $linker, $record, $frameworkcode, C4::Context->preference("CatalogModuleRelink") || '' );
520 # By default we probably don't want to relink things when cataloging
521 return $headings_changed;
524 =head2 LinkBibHeadingsToAuthorities
526 my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode, [$allowrelink]);
528 Links bib headings to authority records by checking
529 each authority-controlled field in the C<MARC::Record>
530 object C<$marc>, looking for a matching authority record,
531 and setting the linking subfield $9 to the ID of that
534 If $allowrelink is false, existing authids will never be
535 replaced, regardless of the values of LinkerKeepStale and
538 Returns the number of heading links changed in the
543 sub LinkBibHeadingsToAuthorities {
546 my $frameworkcode = shift;
547 my $allowrelink = shift;
548 my $tagtolink = shift;
551 carp 'LinkBibHeadingsToAuthorities called on undefined bib record';
555 require C4::AuthoritiesMarc;
557 $allowrelink = 1 unless defined $allowrelink;
558 my $num_headings_changed = 0;
559 foreach my $field ( $bib->fields() ) {
560 if ( defined $tagtolink ) {
561 next unless $field->tag() == $tagtolink ;
563 my $heading = C4::Heading->new_from_field( $field, $frameworkcode );
564 next unless defined $heading;
567 my $current_link = $field->subfield('9');
569 if ( defined $current_link && (!$allowrelink || !C4::Context->preference('LinkerRelink')) )
571 $results{'linked'}->{ $heading->display_form() }++;
575 my ( $authid, $fuzzy, $match_count ) = $linker->get_link($heading);
577 $results{ $fuzzy ? 'fuzzy' : 'linked' }
578 ->{ $heading->display_form() }++;
579 next if defined $current_link and $current_link == $authid;
581 $field->delete_subfield( code => '9' ) if defined $current_link;
582 $field->add_subfields( '9', $authid );
583 $num_headings_changed++;
586 if ( defined $current_link
587 && (!$allowrelink || C4::Context->preference('LinkerKeepStale')) )
589 $results{'fuzzy'}->{ $heading->display_form() }++;
591 elsif ( C4::Context->preference('AutoCreateAuthorities') ) {
592 if ( _check_valid_auth_link( $current_link, $field ) ) {
593 $results{'linked'}->{ $heading->display_form() }++;
595 elsif ( !$match_count ) {
596 my $authority_type = Koha::Authority::Types->find( $heading->auth_type() );
597 my $marcrecordauth = MARC::Record->new();
598 if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
599 $marcrecordauth->leader(' nz a22 o 4500');
600 SetMarcUnicodeFlag( $marcrecordauth, 'MARC21' );
602 $field->delete_subfield( code => '9' )
603 if defined $current_link;
605 foreach my $subfield ( $field->subfields() ){
606 if ( $subfield->[0] =~ /[A-z]/
607 && C4::Heading::valid_heading_subfield(
608 $field->tag, $subfield->[0] )
610 push @auth_subfields, $subfield->[0] => $subfield->[1];
613 # Bib headings contain some ending punctuation that should NOT
614 # be included in the authority record. Strip those before creation
615 next unless @auth_subfields; # Don't try to create a record if we have no fields;
616 my $last_sub = pop @auth_subfields;
617 $last_sub =~ s/[\s]*[,.:=;!%\/][\s]*$//;
618 push @auth_subfields, $last_sub;
619 my $authfield = MARC::Field->new( $authority_type->auth_tag_to_report, '', '', @auth_subfields );
620 $marcrecordauth->insert_fields_ordered($authfield);
622 # bug 2317: ensure new authority knows it's using UTF-8; currently
623 # only need to do this for MARC21, as MARC::Record->as_xml_record() handles
624 # automatically for UNIMARC (by not transcoding)
625 # FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record
626 # use UTF-8, but as of 2008-08-05, did not want to introduce that kind
627 # of change to a core API just before the 3.0 release.
629 if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
630 my $userenv = C4::Context->userenv;
632 if ( $userenv && $userenv->{'branch'} ) {
633 $library = Koha::Libraries->find( $userenv->{'branch'} );
635 $marcrecordauth->insert_fields_ordered(
638 'a' => "Machine generated authority record."
642 $bib->author() . ", "
643 . $bib->title_proper() . ", "
644 . $bib->publication_date() . " ";
645 $cite =~ s/^[\s\,]*//;
646 $cite =~ s/[\s\,]*$//;
649 . ( $library ? $library->get_effective_marcorgcode : C4::Context->preference('MARCOrgCode') ) . ")"
650 . $bib->subfield( '999', 'c' ) . ": "
652 $marcrecordauth->insert_fields_ordered(
653 MARC::Field->new( '670', '', '', 'a' => $cite ) );
656 # warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted;
659 C4::AuthoritiesMarc::AddAuthority( $marcrecordauth, '',
660 $heading->auth_type() );
661 $field->add_subfields( '9', $authid );
662 $num_headings_changed++;
663 $linker->update_cache($heading, $authid);
664 $results{'added'}->{ $heading->display_form() }++;
667 elsif ( defined $current_link ) {
668 if ( _check_valid_auth_link( $current_link, $field ) ) {
669 $results{'linked'}->{ $heading->display_form() }++;
672 $field->delete_subfield( code => '9' );
673 $num_headings_changed++;
674 $results{'unlinked'}->{ $heading->display_form() }++;
678 $results{'unlinked'}->{ $heading->display_form() }++;
683 return $num_headings_changed, \%results;
686 =head2 _check_valid_auth_link
688 if ( _check_valid_auth_link($authid, $field) ) {
692 Check whether the specified heading-auth link is valid without reference
693 to Zebra. Ideally this code would be in C4::Heading, but that won't be
694 possible until we have de-cycled C4::AuthoritiesMarc, so this is the
699 sub _check_valid_auth_link {
700 my ( $authid, $field ) = @_;
701 require C4::AuthoritiesMarc;
703 my $authorized_heading =
704 C4::AuthoritiesMarc::GetAuthorizedHeading( { 'authid' => $authid } ) || '';
705 return ($field->as_string('abcdefghijklmnopqrstuvwxyz') eq $authorized_heading);
710 $data = &GetBiblioData($biblionumber);
712 Returns information about the book with the given biblionumber.
713 C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
714 the C<biblio> and C<biblioitems> tables in the
717 In addition, C<$data-E<gt>{subject}> is the list of the book's
718 subjects, separated by C<" , "> (space, comma, space).
719 If there are multiple biblioitems with the given biblionumber, only
720 the first one is considered.
726 my $dbh = C4::Context->dbh;
728 my $query = " SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
730 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
731 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
732 WHERE biblio.biblionumber = ?";
734 my $sth = $dbh->prepare($query);
735 $sth->execute($bibnum);
737 $data = $sth->fetchrow_hashref;
741 } # sub GetBiblioData
745 $isbd = &GetISBDView({
746 'record' => $marc_record,
747 'template' => $interface, # opac/intranet
748 'framework' => $framework,
751 Return the ISBD view which can be included in opac and intranet
758 # Expecting record WITH items.
759 my $record = $params->{record};
760 return unless defined $record;
762 my $template = $params->{template} // q{};
763 my $sysprefname = $template eq 'opac' ? 'opacisbd' : 'isbd';
764 my $framework = $params->{framework};
765 my $itemtype = $framework;
766 my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch" );
767 my $tagslib = GetMarcStructure( 1, $itemtype, { unsafe => 1 } );
769 my $ISBD = C4::Context->preference($sysprefname);
774 foreach my $isbdfield ( split( /#/, $bloc ) ) {
776 # $isbdfield= /(.?.?.?)/;
777 $isbdfield =~ /(\d\d\d)([^\|])?\|(.*)\|(.*)\|(.*)/;
778 my $fieldvalue = $1 || 0;
779 my $subfvalue = $2 || "";
781 my $analysestring = $4;
784 # warn "==> $1 / $2 / $3 / $4";
785 # my $fieldvalue=substr($isbdfield,0,3);
786 if ( $fieldvalue > 0 ) {
787 my $hasputtextbefore = 0;
788 my @fieldslist = $record->field($fieldvalue);
789 @fieldslist = sort { $a->subfield($holdingbrtagsubf) cmp $b->subfield($holdingbrtagsubf) } @fieldslist if ( $fieldvalue eq $holdingbrtagf );
791 # warn "ERROR IN ISBD DEFINITION at : $isbdfield" unless $fieldvalue;
792 # warn "FV : $fieldvalue";
793 if ( $subfvalue ne "" ) {
794 # OPAC hidden subfield
796 if ( ( $template eq 'opac' )
797 && ( $tagslib->{$fieldvalue}->{$subfvalue}->{'hidden'} || 0 ) > 0 );
798 foreach my $field (@fieldslist) {
799 foreach my $subfield ( $field->subfield($subfvalue) ) {
800 my $calculated = $analysestring;
801 my $tag = $field->tag();
804 my $subfieldvalue = GetAuthorisedValueDesc( $tag, $subfvalue, $subfield, '', $tagslib );
805 my $tagsubf = $tag . $subfvalue;
806 $calculated =~ s/\{(.?.?.?.?)$tagsubf(.*?)\}/$1$subfieldvalue$2\{$1$tagsubf$2\}/g;
807 if ( $template eq "opac" ) { $calculated =~ s#/cgi-bin/koha/[^/]+/([^.]*.pl\?.*)$#opac-$1#g; }
809 # field builded, store the result
810 if ( $calculated && !$hasputtextbefore ) { # put textbefore if not done
811 $blocres .= $textbefore;
812 $hasputtextbefore = 1;
815 # remove punctuation at start
816 $calculated =~ s/^( |;|:|\.|-)*//g;
817 $blocres .= $calculated;
822 $blocres .= $textafter if $hasputtextbefore;
824 foreach my $field (@fieldslist) {
825 my $calculated = $analysestring;
826 my $tag = $field->tag();
829 my @subf = $field->subfields;
830 for my $i ( 0 .. $#subf ) {
831 my $valuecode = $subf[$i][1];
832 my $subfieldcode = $subf[$i][0];
833 # OPAC hidden subfield
835 if ( ( $template eq 'opac' )
836 && ( $tagslib->{$fieldvalue}->{$subfieldcode}->{'hidden'} || 0 ) > 0 );
837 my $subfieldvalue = GetAuthorisedValueDesc( $tag, $subf[$i][0], $subf[$i][1], '', $tagslib );
838 my $tagsubf = $tag . $subfieldcode;
840 $calculated =~ s/ # replace all {{}} codes by the value code.
841 \{\{$tagsubf\}\} # catch the {{actualcode}}
843 $valuecode # replace by the value code
846 $calculated =~ s/\{(.?.?.?.?)$tagsubf(.*?)\}/$1$subfieldvalue$2\{$1$tagsubf$2\}/g;
847 if ( $template eq "opac" ) { $calculated =~ s#/cgi-bin/koha/[^/]+/([^.]*.pl\?.*)$#opac-$1#g; }
850 # field builded, store the result
851 if ( $calculated && !$hasputtextbefore ) { # put textbefore if not done
852 $blocres .= $textbefore;
853 $hasputtextbefore = 1;
856 # remove punctuation at start
857 $calculated =~ s/^( |;|:|\.|-)*//g;
858 $blocres .= $calculated;
861 $blocres .= $textafter if $hasputtextbefore;
864 $blocres .= $isbdfield;
869 $res =~ s/\{(.*?)\}//g;
871 $res =~ s/\n/<br\/>/g;
879 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
881 =head2 IsMarcStructureInternal
883 my $tagslib = C4::Biblio::GetMarcStructure();
884 for my $tag ( sort keys %$tagslib ) {
886 for my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
887 next if IsMarcStructureInternal($tagslib->{$tag}{$subfield});
892 GetMarcStructure creates keys (lib, tab, mandatory, repeatable, important) for a display purpose.
893 These different values should not be processed as valid subfields.
897 sub IsMarcStructureInternal {
898 my ( $subfield ) = @_;
899 return ref $subfield ? 0 : 1;
902 =head2 GetMarcStructure
904 $res = GetMarcStructure($forlibrarian, $frameworkcode, [ $params ]);
906 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
907 $forlibrarian :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
908 $frameworkcode : the framework code to read
909 $params allows you to pass { unsafe => 1 } for better performance.
911 Note: If you call GetMarcStructure with unsafe => 1, do not modify or
912 even autovivify its contents. It is a cached/shared data structure. Your
913 changes c/would be passed around in subsequent calls.
917 sub GetMarcStructure {
918 my ( $forlibrarian, $frameworkcode, $params ) = @_;
919 $frameworkcode = "" unless $frameworkcode;
921 $forlibrarian = $forlibrarian ? 1 : 0;
922 my $unsafe = ($params && $params->{unsafe})? 1: 0;
923 my $cache = Koha::Caches->get_instance();
924 my $cache_key = "MarcStructure-$forlibrarian-$frameworkcode";
925 my $cached = $cache->get_from_cache($cache_key, { unsafe => $unsafe });
926 return $cached if $cached;
928 my $dbh = C4::Context->dbh;
929 my $sth = $dbh->prepare(
930 "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable,important,ind1_defaultvalue,ind2_defaultvalue
931 FROM marc_tag_structure
932 WHERE frameworkcode=?
935 $sth->execute($frameworkcode);
936 my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable, $important, $ind1_defaultvalue, $ind2_defaultvalue );
938 while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable, $important, $ind1_defaultvalue, $ind2_defaultvalue ) = $sth->fetchrow ) {
939 $res->{$tag}->{lib} = ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
940 $res->{$tag}->{tab} = "";
941 $res->{$tag}->{mandatory} = $mandatory;
942 $res->{$tag}->{important} = $important;
943 $res->{$tag}->{repeatable} = $repeatable;
944 $res->{$tag}->{ind1_defaultvalue} = $ind1_defaultvalue;
945 $res->{$tag}->{ind2_defaultvalue} = $ind2_defaultvalue;
948 $sth = $dbh->prepare(
949 "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue,maxlength,important
950 FROM marc_subfield_structure
951 WHERE frameworkcode=?
952 ORDER BY tagfield,tagsubfield
956 $sth->execute($frameworkcode);
959 my $authorised_value;
971 ( $tag, $subfield, $liblibrarian, $libopac, $tab, $mandatory, $repeatable, $authorised_value,
972 $authtypecode, $value_builder, $kohafield, $seealso, $hidden, $isurl, $link, $defaultvalue,
973 $maxlength, $important
977 $res->{$tag}->{$subfield}->{lib} = ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
978 $res->{$tag}->{$subfield}->{tab} = $tab;
979 $res->{$tag}->{$subfield}->{mandatory} = $mandatory;
980 $res->{$tag}->{$subfield}->{important} = $important;
981 $res->{$tag}->{$subfield}->{repeatable} = $repeatable;
982 $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
983 $res->{$tag}->{$subfield}->{authtypecode} = $authtypecode;
984 $res->{$tag}->{$subfield}->{value_builder} = $value_builder;
985 $res->{$tag}->{$subfield}->{kohafield} = $kohafield;
986 $res->{$tag}->{$subfield}->{seealso} = $seealso;
987 $res->{$tag}->{$subfield}->{hidden} = $hidden;
988 $res->{$tag}->{$subfield}->{isurl} = $isurl;
989 $res->{$tag}->{$subfield}->{'link'} = $link;
990 $res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue;
991 $res->{$tag}->{$subfield}->{maxlength} = $maxlength;
994 $cache->set_in_cache($cache_key, $res);
998 =head2 GetUsedMarcStructure
1000 The same function as GetMarcStructure except it just takes field
1001 in tab 0-9. (used field)
1003 my $results = GetUsedMarcStructure($frameworkcode);
1005 C<$results> is a ref to an array which each case contains a ref
1006 to a hash which each keys is the columns from marc_subfield_structure
1008 C<$frameworkcode> is the framework code.
1012 sub GetUsedMarcStructure {
1013 my $frameworkcode = shift || '';
1016 FROM marc_subfield_structure
1018 AND frameworkcode = ?
1019 ORDER BY tagfield, tagsubfield
1021 my $sth = C4::Context->dbh->prepare($query);
1022 $sth->execute($frameworkcode);
1023 return $sth->fetchall_arrayref( {} );
1028 =head2 GetMarcSubfieldStructure
1030 my $structure = GetMarcSubfieldStructure($frameworkcode, [$params]);
1032 Returns a reference to hash representing MARC subfield structure
1033 for framework with framework code C<$frameworkcode>, C<$params> is
1034 optional and may contain additional options.
1038 =item C<$frameworkcode>
1044 An optional hash reference with additional options.
1045 The following options are supported:
1051 Pass { unsafe => 1 } do disable cached object cloning,
1052 and instead get a shared reference, resulting in better
1053 performance (but care must be taken so that retured object
1056 Note: If you call GetMarcSubfieldStructure with unsafe => 1, do not modify or
1057 even autovivify its contents. It is a cached/shared data structure. Your
1058 changes would be passed around in subsequent calls.
1066 sub GetMarcSubfieldStructure {
1067 my ( $frameworkcode, $params ) = @_;
1069 $frameworkcode //= '';
1071 my $cache = Koha::Caches->get_instance();
1072 my $cache_key = "MarcSubfieldStructure-$frameworkcode";
1073 my $cached = $cache->get_from_cache($cache_key, { unsafe => ($params && $params->{unsafe}) });
1074 return $cached if $cached;
1076 my $dbh = C4::Context->dbh;
1077 # We moved to selectall_arrayref since selectall_hashref does not
1078 # keep duplicate mappings on kohafield (like place in 260 vs 264)
1079 my $subfield_aref = $dbh->selectall_arrayref( q|
1081 FROM marc_subfield_structure
1082 WHERE frameworkcode = ?
1084 ORDER BY frameworkcode,tagfield,tagsubfield
1085 |, { Slice => {} }, $frameworkcode );
1086 # Now map the output to a hash structure
1087 my $subfield_structure = {};
1088 foreach my $row ( @$subfield_aref ) {
1089 push @{ $subfield_structure->{ $row->{kohafield} }}, $row;
1091 $cache->set_in_cache( $cache_key, $subfield_structure );
1092 return $subfield_structure;
1095 =head2 GetMarcFromKohaField
1097 ( $field,$subfield ) = GetMarcFromKohaField( $kohafield );
1098 @fields = GetMarcFromKohaField( $kohafield );
1099 $field = GetMarcFromKohaField( $kohafield );
1101 Returns the MARC fields & subfields mapped to $kohafield.
1102 Since the Default framework is considered as authoritative for such
1103 mappings, the former frameworkcode parameter is obsoleted.
1105 In list context all mappings are returned; there can be multiple
1106 mappings. Note that in the above example you could miss a second
1107 mappings in the first call.
1108 In scalar context only the field tag of the first mapping is returned.
1112 sub GetMarcFromKohaField {
1113 my ( $kohafield ) = @_;
1114 return unless $kohafield;
1115 # The next call uses the Default framework since it is AUTHORITATIVE
1116 # for all Koha to MARC mappings.
1117 my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
1119 foreach( @{ $mss->{$kohafield} } ) {
1120 push @retval, $_->{tagfield}, $_->{tagsubfield};
1122 return wantarray ? @retval : ( @retval ? $retval[0] : undef );
1125 =head2 GetMarcSubfieldStructureFromKohaField
1127 my $str = GetMarcSubfieldStructureFromKohaField( $kohafield );
1129 Returns marc subfield structure information for $kohafield.
1130 The Default framework is used, since it is authoritative for kohafield
1132 In list context returns a list of all hashrefs, since there may be
1133 multiple mappings. In scalar context the first hashref is returned.
1137 sub GetMarcSubfieldStructureFromKohaField {
1138 my ( $kohafield ) = @_;
1140 return unless $kohafield;
1142 # The next call uses the Default framework since it is AUTHORITATIVE
1143 # for all Koha to MARC mappings.
1144 my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
1145 return unless $mss->{$kohafield};
1146 return wantarray ? @{$mss->{$kohafield}} : $mss->{$kohafield}->[0];
1149 =head2 GetMarcBiblio
1151 my $record = GetMarcBiblio({
1152 biblionumber => $biblionumber,
1153 embed_items => $embeditems,
1155 borcat => $patron_category });
1157 Returns MARC::Record representing a biblio record, or C<undef> if the
1158 biblionumber doesn't exist.
1160 Both embed_items and opac are optional.
1161 If embed_items is passed and is 1, items are embedded.
1162 If opac is passed and is 1, the record is filtered as needed.
1166 =item C<$biblionumber>
1170 =item C<$embeditems>
1172 set to true to include item information.
1176 set to true to make the result suited for OPAC view. This causes things like
1177 OpacHiddenItems to be applied.
1181 If the OpacHiddenItemsExceptions system preference is set, this patron category
1182 can be used to make visible OPAC items which would be normally hidden.
1183 It only makes sense in combination both embed_items and opac values true.
1192 if (not defined $params) {
1193 carp 'GetMarcBiblio called without parameters';
1197 my $biblionumber = $params->{biblionumber};
1198 my $embeditems = $params->{embed_items} || 0;
1199 my $opac = $params->{opac} || 0;
1200 my $borcat = $params->{borcat} // q{};
1202 if (not defined $biblionumber) {
1203 carp 'GetMarcBiblio called with undefined biblionumber';
1207 my $dbh = C4::Context->dbh;
1208 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=? ");
1209 $sth->execute($biblionumber);
1210 my $row = $sth->fetchrow_hashref;
1211 my $biblioitemnumber = $row->{'biblioitemnumber'};
1212 my $marcxml = GetXmlBiblio( $biblionumber );
1213 $marcxml = StripNonXmlChars( $marcxml );
1214 my $frameworkcode = GetFrameworkCode($biblionumber);
1215 MARC::File::XML->default_record_format( C4::Context->preference('marcflavour') );
1216 my $record = MARC::Record->new();
1220 MARC::Record::new_from_xml( $marcxml, "UTF-8",
1221 C4::Context->preference('marcflavour') );
1223 if ($@) { warn " problem with :$biblionumber : $@ \n$marcxml"; }
1224 return unless $record;
1226 C4::Biblio::_koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber,
1227 $biblioitemnumber );
1228 C4::Biblio::EmbedItemsInMarcBiblio({
1229 marc_record => $record,
1230 biblionumber => $biblionumber,
1232 borcat => $borcat })
1244 my $marcxml = GetXmlBiblio($biblionumber);
1246 Returns biblio_metadata.metadata/marcxml of the biblionumber passed in parameter.
1247 The XML should only contain biblio information (item information is no longer stored in marcxml field)
1252 my ($biblionumber) = @_;
1253 my $dbh = C4::Context->dbh;
1254 return unless $biblionumber;
1255 my ($marcxml) = $dbh->selectrow_array(
1258 FROM biblio_metadata
1259 WHERE biblionumber=?
1260 AND format='marcxml'
1262 |, undef, $biblionumber, C4::Context->preference('marcflavour')
1269 return the prices in accordance with the Marc format.
1271 returns 0 if no price found
1272 returns undef if called without a marc record or with
1273 an unrecognized marc format
1278 my ( $record, $marcflavour ) = @_;
1280 carp 'GetMarcPrice called on undefined record';
1287 if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
1288 @listtags = ('345', '020');
1290 } elsif ( $marcflavour eq "UNIMARC" ) {
1291 @listtags = ('345', '010');
1297 for my $field ( $record->field(@listtags) ) {
1298 for my $subfield_value ($field->subfield($subfield)){
1300 $subfield_value = MungeMarcPrice( $subfield_value );
1301 return $subfield_value if ($subfield_value);
1304 return 0; # no price found
1307 =head2 MungeMarcPrice
1309 Return the best guess at what the actual price is from a price field.
1313 sub MungeMarcPrice {
1315 return unless ( $price =~ m/\d/ ); ## No digits means no price.
1316 # Look for the currency symbol and the normalized code of the active currency, if it's there,
1317 my $active_currency = Koha::Acquisition::Currencies->get_active;
1318 my $symbol = $active_currency->symbol;
1319 my $isocode = $active_currency->isocode;
1320 $isocode = $active_currency->currency unless defined $isocode;
1323 my @matches =($price=~ /
1325 ( # start of capturing parenthesis
1327 (?:[\p{Sc}\p{L}\/.]){1,4} # any character from Currency signs or Letter Unicode categories or slash or dot within 1 to 4 occurrences : call this whole block 'symbol block'
1328 |(?:\d+[\p{P}\s]?){1,4} # or else at least one digit followed or not by a punctuation sign or whitespace, all these within 1 to 4 occurrences : call this whole block 'digits block'
1330 \s?\p{Sc}?\s? # followed or not by a whitespace. \p{Sc}?\s? are for cases like '25$ USD'
1332 (?:[\p{Sc}\p{L}\/.]){1,4} # followed by same block as symbol block
1333 |(?:\d+[\p{P}\s]?){1,4} # or by same block as digits block
1335 \s?\p{L}{0,4}\s? # followed or not by a whitespace. \p{L}{0,4}\s? are for cases like '$9.50 USD'
1336 ) # end of capturing parenthesis
1337 (?:\p{P}|\z) # followed by a punctuation sign or by the end of the string
1341 foreach ( @matches ) {
1342 $localprice = $_ and last if index($_, $isocode)>=0;
1344 if ( !$localprice ) {
1345 foreach ( @matches ) {
1346 $localprice = $_ and last if $_=~ /(^|[^\p{Sc}\p{L}\/])\Q$symbol\E([^\p{Sc}\p{L}\/]+\z|\z)/;
1351 if ( $localprice ) {
1352 $price = $localprice;
1354 ## Grab the first number in the string ( can use commas or periods for thousands separator and/or decimal separator )
1355 ( $price ) = $price =~ m/([\d\,\.]+[[\,\.]\d\d]?)/;
1357 # eliminate symbol/isocode, space and any final dot from the string
1358 $price =~ s/[\p{Sc}\p{L}\/ ]|\.$//g;
1359 # remove comma,dot when used as separators from hundreds
1360 $price =~s/[\,\.](\d{3})/$1/g;
1361 # convert comma to dot to ensure correct display of decimals if existing
1367 =head2 GetMarcQuantity
1369 return the quantity of a book. Used in acquisition only, when importing a file an iso2709 from a bookseller
1370 Warning : this is not really in the marc standard. In Unimarc, Electre (the most widely used bookseller) use the 969$a
1372 returns 0 if no quantity found
1373 returns undef if called without a marc record or with
1374 an unrecognized marc format
1378 sub GetMarcQuantity {
1379 my ( $record, $marcflavour ) = @_;
1381 carp 'GetMarcQuantity called on undefined record';
1388 if ( $marcflavour eq "MARC21" ) {
1390 } elsif ( $marcflavour eq "UNIMARC" ) {
1391 @listtags = ('969');
1397 for my $field ( $record->field(@listtags) ) {
1398 for my $subfield_value ($field->subfield($subfield)){
1400 if ($subfield_value) {
1401 # in France, the cents separator is the , but sometimes, ppl use a .
1402 # in this case, the price will be x100 when unformatted ! Replace the . by a , to get a proper price calculation
1403 $subfield_value =~ s/\./,/ if C4::Context->preference("CurrencyFormat") eq "FR";
1404 return $subfield_value;
1408 return 0; # no price found
1412 =head2 GetAuthorisedValueDesc
1414 my $subfieldvalue =get_authorised_value_desc(
1415 $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category, $opac);
1417 Retrieve the complete description for a given authorised value.
1419 Now takes $category and $value pair too.
1421 my $auth_value_desc =GetAuthorisedValueDesc(
1422 '','', 'DVD' ,'','','CCODE');
1424 If the optional $opac parameter is set to a true value, displays OPAC
1425 descriptions rather than normal ones when they exist.
1429 sub GetAuthorisedValueDesc {
1430 my ( $tag, $subfield, $value, $framework, $tagslib, $category, $opac ) = @_;
1434 return $value unless defined $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1437 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1438 my $branch = Koha::Libraries->find($value);
1439 return $branch? $branch->branchname: q{};
1443 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1444 my $itemtype = Koha::ItemTypes->find( $value );
1445 return $itemtype ? $itemtype->translated_description : q||;
1448 #---- "true" authorized value
1449 $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1452 my $dbh = C4::Context->dbh;
1453 if ( $category ne "" ) {
1454 my $sth = $dbh->prepare( "SELECT lib, lib_opac FROM authorised_values WHERE category = ? AND authorised_value = ?" );
1455 $sth->execute( $category, $value );
1456 my $data = $sth->fetchrow_hashref;
1457 return ( $opac && $data->{'lib_opac'} ) ? $data->{'lib_opac'} : $data->{'lib'};
1459 return $value; # if nothing is found return the original value
1463 =head2 GetMarcControlnumber
1465 $marccontrolnumber = GetMarcControlnumber($record,$marcflavour);
1467 Get the control number / record Identifier from the MARC record and return it.
1471 sub GetMarcControlnumber {
1472 my ( $record, $marcflavour ) = @_;
1474 carp 'GetMarcControlnumber called on undefined record';
1477 my $controlnumber = "";
1478 # Control number or Record identifier are the same field in MARC21, UNIMARC and NORMARC
1479 # Keep $marcflavour for possible later use
1480 if ($marcflavour eq "MARC21" || $marcflavour eq "UNIMARC" || $marcflavour eq "NORMARC") {
1481 my $controlnumberField = $record->field('001');
1482 if ($controlnumberField) {
1483 $controlnumber = $controlnumberField->data();
1486 return $controlnumber;
1491 $marcisbnsarray = GetMarcISBN( $record, $marcflavour );
1493 Get all ISBNs from the MARC record and returns them in an array.
1494 ISBNs stored in different fields depending on MARC flavour
1499 my ( $record, $marcflavour ) = @_;
1501 carp 'GetMarcISBN called on undefined record';
1505 if ( $marcflavour eq "UNIMARC" ) {
1507 } else { # assume marc21 if not unimarc
1512 foreach my $field ( $record->field($scope) ) {
1513 my $isbn = $field->subfield( 'a' );
1514 if ( $isbn && $isbn ne "" ) {
1515 push @marcisbns, $isbn;
1525 $marcissnsarray = GetMarcISSN( $record, $marcflavour );
1527 Get all valid ISSNs from the MARC record and returns them in an array.
1528 ISSNs are stored in different fields depending on MARC flavour
1533 my ( $record, $marcflavour ) = @_;
1535 carp 'GetMarcISSN called on undefined record';
1539 if ( $marcflavour eq "UNIMARC" ) {
1542 else { # assume MARC21 or NORMARC
1546 foreach my $field ( $record->field($scope) ) {
1547 push @marcissns, $field->subfield( 'a' )
1548 if ( $field->subfield( 'a' ) ne "" );
1555 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1557 Get all notes from the MARC record and returns them in an array.
1558 The notes are stored in different fields depending on MARC flavour.
1559 MARC21 5XX $u subfields receive special attention as they are URIs.
1564 my ( $record, $marcflavour, $opac ) = @_;
1566 carp 'GetMarcNotes called on undefined record';
1570 my $scope = $marcflavour eq "UNIMARC"? '3..': '5..';
1573 #MARC21 specs indicate some notes should be private if first indicator 0
1574 my %maybe_private = (
1582 my %hiddenlist = map { $_ => 1 }
1583 split( /,/, C4::Context->preference('NotesToHide'));
1584 foreach my $field ( $record->field($scope) ) {
1585 my $tag = $field->tag();
1586 next if $hiddenlist{ $tag };
1587 next if $opac && $maybe_private{$tag} && !$field->indicator(1);
1588 if( $marcflavour ne 'UNIMARC' && $field->subfield('u') ) {
1589 # Field 5XX$u always contains URI
1590 # Examples: 505u, 506u, 510u, 514u, 520u, 530u, 538u, 540u, 542u, 552u, 555u, 561u, 563u, 583u
1591 # We first push the other subfields, then all $u's separately
1592 # Leave further actions to the template (see e.g. opac-detail)
1594 join '', ( 'a' .. 't', 'v' .. 'z', '0' .. '9' ); # excl 'u'
1595 push @marcnotes, { marcnote => $field->as_string($othersub) };
1596 foreach my $sub ( $field->subfield('u') ) {
1597 $sub =~ s/^\s+|\s+$//g; # trim
1598 push @marcnotes, { marcnote => $sub };
1601 push @marcnotes, { marcnote => $field->as_string() };
1607 =head2 GetMarcSubjects
1609 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1611 Get all subjects from the MARC record and returns them in an array.
1612 The subjects are stored in different fields depending on MARC flavour
1616 sub GetMarcSubjects {
1617 my ( $record, $marcflavour ) = @_;
1619 carp 'GetMarcSubjects called on undefined record';
1622 my ( $mintag, $maxtag, $fields_filter );
1623 if ( $marcflavour eq "UNIMARC" ) {
1626 $fields_filter = '6..';
1627 } else { # marc21/normarc
1630 $fields_filter = '6..';
1635 my $subject_limit = C4::Context->preference("TraceCompleteSubfields") ? 'su,complete-subfield' : 'su';
1636 my $AuthoritySeparator = C4::Context->preference('AuthoritySeparator');
1638 foreach my $field ( $record->field($fields_filter) ) {
1639 next unless ($field->tag() >= $mintag && $field->tag() <= $maxtag);
1641 my @subfields = $field->subfields();
1644 # if there is an authority link, build the links with an= subfield9
1645 my $subfield9 = $field->subfield('9');
1648 my $linkvalue = $subfield9;
1649 $linkvalue =~ s/(\(|\))//g;
1650 @link_loop = ( { limit => 'an', 'link' => $linkvalue } );
1651 $authoritylink = $linkvalue
1655 for my $subject_subfield (@subfields) {
1656 next if ( $subject_subfield->[0] eq '9' );
1658 # don't load unimarc subfields 3,4,5
1659 next if ( ( $marcflavour eq "UNIMARC" ) and ( $subject_subfield->[0] =~ /2|3|4|5/ ) );
1660 # don't load MARC21 subfields 2 (FIXME: any more subfields??)
1661 next if ( ( $marcflavour eq "MARC21" ) and ( $subject_subfield->[0] =~ /2/ ) );
1663 my $code = $subject_subfield->[0];
1664 my $value = $subject_subfield->[1];
1665 my $linkvalue = $value;
1666 $linkvalue =~ s/(\(|\))//g;
1667 # if no authority link, build a search query
1668 unless ($subfield9) {
1670 limit => $subject_limit,
1671 'link' => $linkvalue,
1672 operator => (scalar @link_loop) ? ' and ' : undef
1675 my @this_link_loop = @link_loop;
1677 unless ( $code eq '0' ) {
1678 push @subfields_loop, {
1681 link_loop => \@this_link_loop,
1682 separator => (scalar @subfields_loop) ? $AuthoritySeparator : ''
1687 push @marcsubjects, {
1688 MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop,
1689 authoritylink => $authoritylink,
1690 } if $authoritylink || @subfields_loop;
1693 return \@marcsubjects;
1694 } #end getMARCsubjects
1696 =head2 GetMarcAuthors
1698 authors = GetMarcAuthors($record,$marcflavour);
1700 Get all authors from the MARC record and returns them in an array.
1701 The authors are stored in different fields depending on MARC flavour
1705 sub GetMarcAuthors {
1706 my ( $record, $marcflavour ) = @_;
1708 carp 'GetMarcAuthors called on undefined record';
1711 my ( $mintag, $maxtag, $fields_filter );
1713 # tagslib useful only for UNIMARC author responsibilities
1715 if ( $marcflavour eq "UNIMARC" ) {
1716 # FIXME : we don't have the framework available, we take the default framework. May be buggy on some setups, will be usually correct.
1717 $tagslib = GetMarcStructure( 1, '', { unsafe => 1 });
1720 $fields_filter = '7..';
1721 } else { # marc21/normarc
1724 $fields_filter = '7..';
1728 my $AuthoritySeparator = C4::Context->preference('AuthoritySeparator');
1730 foreach my $field ( $record->field($fields_filter) ) {
1731 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1734 my @subfields = $field->subfields();
1737 # if there is an authority link, build the link with Koha-Auth-Number: subfield9
1738 my $subfield9 = $field->subfield('9');
1740 my $linkvalue = $subfield9;
1741 $linkvalue =~ s/(\(|\))//g;
1742 @link_loop = ( { 'limit' => 'an', 'link' => $linkvalue } );
1747 for my $authors_subfield (@subfields) {
1748 next if ( $authors_subfield->[0] eq '9' );
1750 # unimarc3 contains the $3 of the author for UNIMARC.
1751 # For french academic libraries, it's the "ppn", and it's required for idref webservice
1752 $unimarc3 = $authors_subfield->[1] if $marcflavour eq 'UNIMARC' and $authors_subfield->[0] =~ /3/;
1754 # don't load unimarc subfields 3, 5
1755 next if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /3|5/ ) );
1757 my $code = $authors_subfield->[0];
1758 my $value = $authors_subfield->[1];
1759 my $linkvalue = $value;
1760 $linkvalue =~ s/(\(|\))//g;
1761 # UNIMARC author responsibility
1762 if ( $marcflavour eq 'UNIMARC' and $code eq '4' ) {
1763 $value = GetAuthorisedValueDesc( $field->tag(), $code, $value, '', $tagslib );
1764 $linkvalue = "($value)";
1766 # if no authority link, build a search query
1767 unless ($subfield9) {
1770 'link' => $linkvalue,
1771 operator => (scalar @link_loop) ? ' and ' : undef
1774 my @this_link_loop = @link_loop;
1776 unless ( $code eq '0') {
1777 push @subfields_loop, {
1778 tag => $field->tag(),
1781 link_loop => \@this_link_loop,
1782 separator => (scalar @subfields_loop) ? $AuthoritySeparator : ''
1786 push @marcauthors, {
1787 MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop,
1788 authoritylink => $subfield9,
1789 unimarc3 => $unimarc3
1792 return \@marcauthors;
1797 $marcurls = GetMarcUrls($record,$marcflavour);
1799 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1800 Assumes web resources (not uncommon in MARC21 to omit resource type ind)
1805 my ( $record, $marcflavour ) = @_;
1807 carp 'GetMarcUrls called on undefined record';
1812 for my $field ( $record->field('856') ) {
1814 for my $note ( $field->subfield('z') ) {
1815 push @notes, { note => $note };
1817 my @urls = $field->subfield('u');
1818 foreach my $url (@urls) {
1819 $url =~ s/^\s+|\s+$//g; # trim
1821 if ( $marcflavour eq 'MARC21' ) {
1822 my $s3 = $field->subfield('3');
1823 my $link = $field->subfield('y');
1824 unless ( $url =~ /^\w+:/ ) {
1825 if ( $field->indicator(1) eq '7' ) {
1826 $url = $field->subfield('2') . "://" . $url;
1827 } elsif ( $field->indicator(1) eq '1' ) {
1828 $url = 'ftp://' . $url;
1831 # properly, this should be if ind1=4,
1832 # however we will assume http protocol since we're building a link.
1833 $url = 'http://' . $url;
1837 # TODO handle ind 2 (relationship)
1842 $marcurl->{'linktext'} = $link || $s3 || C4::Context->preference('URLLinkText') || $url;
1843 $marcurl->{'part'} = $s3 if ($link);
1844 $marcurl->{'toc'} = 1 if ( defined($s3) && $s3 =~ /^[Tt]able/ );
1846 $marcurl->{'linktext'} = $field->subfield('2') || C4::Context->preference('URLLinkText') || $url;
1847 $marcurl->{'MARCURL'} = $url;
1849 push @marcurls, $marcurl;
1855 =head2 GetMarcSeries
1857 $marcseriesarray = GetMarcSeries($record,$marcflavour);
1859 Get all series from the MARC record and returns them in an array.
1860 The series are stored in different fields depending on MARC flavour
1865 my ( $record, $marcflavour ) = @_;
1867 carp 'GetMarcSeries called on undefined record';
1871 my ( $mintag, $maxtag, $fields_filter );
1872 if ( $marcflavour eq "UNIMARC" ) {
1875 $fields_filter = '2..';
1876 } else { # marc21/normarc
1879 $fields_filter = '4..';
1883 my $AuthoritySeparator = C4::Context->preference('AuthoritySeparator');
1885 foreach my $field ( $record->field($fields_filter) ) {
1886 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1888 my @subfields = $field->subfields();
1891 for my $series_subfield (@subfields) {
1893 # ignore $9, used for authority link
1894 next if ( $series_subfield->[0] eq '9' );
1897 my $code = $series_subfield->[0];
1898 my $value = $series_subfield->[1];
1899 my $linkvalue = $value;
1900 $linkvalue =~ s/(\(|\))//g;
1902 # see if this is an instance of a volume
1903 if ( $code eq 'v' ) {
1908 'link' => $linkvalue,
1909 operator => (scalar @link_loop) ? ' and ' : undef
1912 if ($volume_number) {
1913 push @subfields_loop, { volumenum => $value };
1915 push @subfields_loop, {
1918 link_loop => \@link_loop,
1919 separator => (scalar @subfields_loop) ? $AuthoritySeparator : '',
1920 volumenum => $volume_number,
1924 push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1927 return \@marcseries;
1928 } #end getMARCseriess
1930 =head2 UpsertMarcSubfield
1932 my $record = C4::Biblio::UpsertMarcSubfield($MARC::Record, $fieldTag, $subfieldCode, $subfieldContent);
1936 sub UpsertMarcSubfield {
1937 my ($record, $tag, $code, $content) = @_;
1938 my $f = $record->field($tag);
1941 $f->update( $code => $content );
1944 my $f = MARC::Field->new( $tag, '', '', $code => $content);
1945 $record->insert_fields_ordered( $f );
1949 =head2 UpsertMarcControlField
1951 my $record = C4::Biblio::UpsertMarcControlField($MARC::Record, $fieldTag, $content);
1955 sub UpsertMarcControlField {
1956 my ($record, $tag, $content) = @_;
1957 die "UpsertMarcControlField() \$tag '$tag' is not a control field\n" unless 0+$tag < 10;
1958 my $f = $record->field($tag);
1961 $f->update( $content );
1964 my $f = MARC::Field->new($tag, $content);
1965 $record->insert_fields_ordered( $f );
1969 =head2 GetFrameworkCode
1971 $frameworkcode = GetFrameworkCode( $biblionumber )
1975 sub GetFrameworkCode {
1976 my ($biblionumber) = @_;
1977 my $dbh = C4::Context->dbh;
1978 my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
1979 $sth->execute($biblionumber);
1980 my ($frameworkcode) = $sth->fetchrow;
1981 return $frameworkcode;
1984 =head2 TransformKohaToMarc
1986 $record = TransformKohaToMarc( $hash [, $params ] )
1988 This function builds a (partial) MARC::Record from a hash.
1989 Hash entries can be from biblio, biblioitems or items.
1990 The params hash includes the parameter no_split used in C4::Items.
1992 This function is called in acquisition module, to create a basic catalogue
1993 entry from user entry.
1998 sub TransformKohaToMarc {
1999 my ( $hash, $params ) = @_;
2000 my $record = MARC::Record->new();
2001 SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
2003 # In the next call we use the Default framework, since it is considered
2004 # authoritative for Koha to Marc mappings.
2005 my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # do not change framework
2007 while ( my ($kohafield, $value) = each %$hash ) {
2008 foreach my $fld ( @{ $mss->{$kohafield} } ) {
2009 my $tagfield = $fld->{tagfield};
2010 my $tagsubfield = $fld->{tagsubfield};
2013 # BZ 21800: split value if field is repeatable.
2014 my @values = _check_split($params, $fld, $value)
2015 ? split(/\s?\|\s?/, $value, -1)
2017 foreach my $value ( @values ) {
2018 next if $value eq '';
2019 $tag_hr->{$tagfield} //= [];
2020 push @{$tag_hr->{$tagfield}}, [($tagsubfield, $value)];
2024 foreach my $tag (sort keys %$tag_hr) {
2025 my @sfl = @{$tag_hr->{$tag}};
2026 @sfl = sort { $a->[0] cmp $b->[0]; } @sfl;
2027 @sfl = map { @{$_}; } @sfl;
2028 # Special care for control fields: remove the subfield indication @
2029 # and do not insert indicators.
2030 my @ind = $tag < 10 ? () : ( " ", " " );
2031 @sfl = grep { $_ ne '@' } @sfl if $tag < 10;
2032 $record->insert_fields_ordered( MARC::Field->new($tag, @ind, @sfl) );
2038 # Checks if $value must be split; may consult passed framework
2039 my ($params, $fld, $value) = @_;
2040 return if index($value,'|') == -1; # nothing to worry about
2041 return if $params->{no_split};
2043 # if we did not get a specific framework, check default in $mss
2044 return $fld->{repeatable} if !$params->{framework};
2046 # here we need to check the specific framework
2047 my $mss = GetMarcSubfieldStructure($params->{framework}, { unsafe => 1 });
2048 foreach my $fld2 ( @{ $mss->{ $fld->{kohafield} } } ) {
2049 next if $fld2->{tagfield} ne $fld->{tagfield};
2050 next if $fld2->{tagsubfield} ne $fld->{tagsubfield};
2051 return 1 if $fld2->{repeatable};
2056 =head2 PrepHostMarcField
2058 $hostfield = PrepHostMarcField ( $hostbiblionumber,$hostitemnumber,$marcflavour )
2060 This function returns a host field populated with data from the host record, the field can then be added to an analytical record
2064 sub PrepHostMarcField {
2065 my ($hostbiblionumber,$hostitemnumber, $marcflavour) = @_;
2066 $marcflavour ||="MARC21";
2068 my $hostrecord = GetMarcBiblio({ biblionumber => $hostbiblionumber });
2069 my $item = Koha::Items->find($hostitemnumber);
2072 if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
2076 if ($hostrecord->subfield('100','a')){
2077 $mainentry = $hostrecord->subfield('100','a');
2078 } elsif ($hostrecord->subfield('110','a')){
2079 $mainentry = $hostrecord->subfield('110','a');
2081 $mainentry = $hostrecord->subfield('111','a');
2084 # qualification info
2086 if (my $field260 = $hostrecord->field('260')){
2087 $qualinfo = $field260->as_string( 'abc' );
2092 my $ed = $hostrecord->subfield('250','a');
2093 my $barcode = $item->barcode;
2094 my $title = $hostrecord->subfield('245','a');
2096 # record control number, 001 with 003 and prefix
2098 if ($hostrecord->field('001')){
2099 $recctrlno = $hostrecord->field('001')->data();
2100 if ($hostrecord->field('003')){
2101 $recctrlno = '('.$hostrecord->field('003')->data().')'.$recctrlno;
2106 my $issn = $hostrecord->subfield('022','a');
2107 my $isbn = $hostrecord->subfield('020','a');
2110 $hostmarcfield = MARC::Field->new(
2112 '0' => $hostbiblionumber,
2113 '9' => $hostitemnumber,
2123 } elsif ($marcflavour eq "UNIMARC") {
2124 $hostmarcfield = MARC::Field->new(
2126 '0' => $hostbiblionumber,
2127 't' => $hostrecord->subfield('200','a'),
2128 '9' => $hostitemnumber
2132 return $hostmarcfield;
2135 =head2 TransformHtmlToXml
2137 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator,
2138 $ind_tag, $auth_type )
2140 $auth_type contains :
2144 =item - nothing : rebuild a biblio. In UNIMARC the encoding is in 100$a pos 26/27
2146 =item - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
2148 =item - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
2154 sub TransformHtmlToXml {
2155 my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
2156 # NOTE: The parameter $ind_tag is NOT USED -- BZ 11247
2158 my $xml = MARC::File::XML::header('UTF-8');
2159 $xml .= "<record>\n";
2160 $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
2161 MARC::File::XML->default_record_format($auth_type);
2163 # in UNIMARC, field 100 contains the encoding
2164 # check that there is one, otherwise the
2165 # MARC::Record->new_from_xml will fail (and Koha will die)
2166 my $unimarc_and_100_exist = 0;
2167 $unimarc_and_100_exist = 1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
2172 for ( my $i = 0 ; $i < @$tags ; $i++ ) {
2174 if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a" ) {
2176 # if we have a 100 field and it's values are not correct, skip them.
2177 # if we don't have any valid 100 field, we will create a default one at the end
2178 my $enc = substr( @$values[$i], 26, 2 );
2179 if ( $enc eq '01' or $enc eq '50' or $enc eq '03' ) {
2180 $unimarc_and_100_exist = 1;
2185 @$values[$i] =~ s/&/&/g;
2186 @$values[$i] =~ s/</</g;
2187 @$values[$i] =~ s/>/>/g;
2188 @$values[$i] =~ s/"/"/g;
2189 @$values[$i] =~ s/'/'/g;
2191 if ( ( @$tags[$i] ne $prevtag ) ) {
2192 $close_last_tag = 0;
2193 $j++ unless ( @$tags[$i] eq "" );
2194 my $str = ( $indicator->[$j] // q{} ) . ' '; # extra space prevents substr outside of string warn
2195 my $ind1 = _default_ind_to_space( substr( $str, 0, 1 ) );
2196 my $ind2 = _default_ind_to_space( substr( $str, 1, 1 ) );
2198 $xml .= "</datafield>\n";
2199 if ( ( @$tags[$i] && @$tags[$i] > 10 )
2200 && ( @$values[$i] ne "" ) ) {
2201 $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2202 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2204 $close_last_tag = 1;
2209 if ( @$values[$i] ne "" ) {
2212 if ( @$tags[$i] eq "000" ) {
2213 $xml .= "<leader>@$values[$i]</leader>\n";
2216 # rest of the fixed fields
2217 } elsif ( @$tags[$i] < 10 ) {
2218 $xml .= "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2221 $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2222 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2224 $close_last_tag = 1;
2228 } else { # @$tags[$i] eq $prevtag
2229 if ( @$values[$i] eq "" ) {
2232 my $str = ( $indicator->[$j] // q{} ) . ' '; # extra space prevents substr outside of string warn
2233 my $ind1 = _default_ind_to_space( substr( $str, 0, 1 ) );
2234 my $ind2 = _default_ind_to_space( substr( $str, 1, 1 ) );
2235 $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2237 $close_last_tag = 1;
2239 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2242 $prevtag = @$tags[$i];
2244 $xml .= "</datafield>\n" if $close_last_tag;
2245 if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist ) {
2247 # warn "SETTING 100 for $auth_type";
2248 my $string = strftime( "%Y%m%d", localtime(time) );
2250 # set 50 to position 26 is biblios, 13 if authorities
2252 $pos = 13 if $auth_type eq 'UNIMARCAUTH';
2253 $string = sprintf( "%-*s", 35, $string );
2254 substr( $string, $pos, 6, "50" );
2255 $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2256 $xml .= "<subfield code=\"a\">$string</subfield>\n";
2257 $xml .= "</datafield>\n";
2259 $xml .= "</record>\n";
2260 $xml .= MARC::File::XML::footer();
2264 =head2 _default_ind_to_space
2266 Passed what should be an indicator returns a space
2267 if its undefined or zero length
2271 sub _default_ind_to_space {
2273 if ( !defined $s || $s eq q{} ) {
2279 =head2 TransformHtmlToMarc
2281 L<$record> = TransformHtmlToMarc(L<$cgi>)
2282 L<$cgi> is the CGI object which contains the values for subfields
2284 'tag_010_indicator1_531951' ,
2285 'tag_010_indicator2_531951' ,
2286 'tag_010_code_a_531951_145735' ,
2287 'tag_010_subfield_a_531951_145735' ,
2288 'tag_200_indicator1_873510' ,
2289 'tag_200_indicator2_873510' ,
2290 'tag_200_code_a_873510_673465' ,
2291 'tag_200_subfield_a_873510_673465' ,
2292 'tag_200_code_b_873510_704318' ,
2293 'tag_200_subfield_b_873510_704318' ,
2294 'tag_200_code_e_873510_280822' ,
2295 'tag_200_subfield_e_873510_280822' ,
2296 'tag_200_code_f_873510_110730' ,
2297 'tag_200_subfield_f_873510_110730' ,
2299 L<$record> is the MARC::Record object.
2303 sub TransformHtmlToMarc {
2304 my ($cgi, $isbiblio) = @_;
2306 my @params = $cgi->multi_param();
2308 # explicitly turn on the UTF-8 flag for all
2309 # 'tag_' parameters to avoid incorrect character
2310 # conversion later on
2311 my $cgi_params = $cgi->Vars;
2312 foreach my $param_name ( keys %$cgi_params ) {
2313 if ( $param_name =~ /^tag_/ ) {
2314 my $param_value = $cgi_params->{$param_name};
2315 unless ( Encode::is_utf8( $param_value ) ) {
2316 $cgi_params->{$param_name} = Encode::decode('UTF-8', $param_value );
2321 # creating a new record
2322 my $record = MARC::Record->new();
2324 my ($biblionumbertagfield, $biblionumbertagsubfield) = (-1, -1);
2325 ($biblionumbertagfield, $biblionumbertagsubfield) =
2326 &GetMarcFromKohaField( "biblio.biblionumber", '' ) if $isbiblio;
2327 #FIXME This code assumes that the CGI params will be in the same order as the fields in the template; this is no absolute guarantee!
2328 for (my $i = 0; $params[$i]; $i++ ) { # browse all CGI params
2329 my $param = $params[$i];
2332 # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
2333 if ( $param eq 'biblionumber' ) {
2334 if ( $biblionumbertagfield < 10 ) {
2335 $newfield = MARC::Field->new( $biblionumbertagfield, scalar $cgi->param($param), );
2337 $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => scalar $cgi->param($param), );
2339 push @fields, $newfield if ($newfield);
2340 } elsif ( $param =~ /^tag_(\d*)_indicator1_/ ) { # new field start when having 'input name="..._indicator1_..."
2343 my $ind1 = _default_ind_to_space( substr( $cgi->param($param), 0, 1 ) );
2344 my $ind2 = _default_ind_to_space( substr( $cgi->param( $params[ $i + 1 ] ), 0, 1 ) );
2348 if ( $tag < 10 ) { # no code for theses fields
2349 # in MARC editor, 000 contains the leader.
2350 next if $tag == $biblionumbertagfield;
2351 my $fval= $cgi->param($params[$j+1]);
2352 if ( $tag eq '000' ) {
2353 # Force a fake leader even if not provided to avoid crashing
2354 # during decoding MARC record containing UTF-8 characters
2356 length( $fval ) == 24
2361 # between 001 and 009 (included)
2362 } elsif ( $fval ne '' ) {
2363 $newfield = MARC::Field->new( $tag, $fval, );
2366 # > 009, deal with subfields
2368 # browse subfields for this tag (reason for _code_ match)
2369 while(defined $params[$j] && $params[$j] =~ /_code_/) {
2370 last unless defined $params[$j+1];
2372 if $tag == $biblionumbertagfield and
2373 $cgi->param($params[$j]) eq $biblionumbertagsubfield;
2374 #if next param ne subfield, then it was probably empty
2375 #try next param by incrementing j
2376 if($params[$j+1]!~/_subfield_/) {$j++; next; }
2377 my $fkey= $cgi->param($params[$j]);
2378 my $fval= $cgi->param($params[$j+1]);
2379 #check if subfield value not empty and field exists
2380 if($fval ne '' && $newfield) {
2381 $newfield->add_subfields( $fkey => $fval);
2383 elsif($fval ne '') {
2384 $newfield = MARC::Field->new( $tag, $ind1, $ind2, $fkey => $fval );
2388 $i= $j-1; #update i for outer loop accordingly
2390 push @fields, $newfield if ($newfield);
2394 @fields = sort { $a->tag() cmp $b->tag() } @fields;
2395 $record->append_fields(@fields);
2399 =head2 TransformMarcToKoha
2401 $result = TransformMarcToKoha( $record, undef, $limit )
2403 Extract data from a MARC bib record into a hashref representing
2404 Koha biblio, biblioitems, and items fields.
2406 If passed an undefined record will log the error and return an empty
2411 sub TransformMarcToKoha {
2412 my ( $record, $frameworkcode, $limit_table ) = @_;
2413 # FIXME Parameter $frameworkcode is obsolete and will be removed
2414 $limit_table //= q{};
2417 if (!defined $record) {
2418 carp('TransformMarcToKoha called with undefined record');
2422 my %tables = ( biblio => 1, biblioitems => 1, items => 1 );
2423 if( $limit_table eq 'items' ) {
2424 %tables = ( items => 1 );
2427 # The next call acknowledges Default as the authoritative framework
2428 # for Koha to MARC mappings.
2429 my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
2430 foreach my $kohafield ( keys %{ $mss } ) {
2431 my ( $table, $column ) = split /[.]/, $kohafield, 2;
2432 next unless $tables{$table};
2433 my $val = TransformMarcToKohaOneField( $kohafield, $record );
2434 next if !defined $val;
2435 my $key = _disambiguate( $table, $column );
2436 $result->{$key} = $val;
2441 =head2 _disambiguate
2443 $newkey = _disambiguate($table, $field);
2445 This is a temporary hack to distinguish between the
2446 following sets of columns when using TransformMarcToKoha.
2448 items.cn_source & biblioitems.cn_source
2449 items.cn_sort & biblioitems.cn_sort
2451 Columns that are currently NOT distinguished (FIXME
2452 due to lack of time to fully test) are:
2454 biblio.notes and biblioitems.notes
2459 FIXME - this is necessary because prefixing each column
2460 name with the table name would require changing lots
2461 of code and templates, and exposing more of the DB
2462 structure than is good to the UI templates, particularly
2463 since biblio and bibloitems may well merge in a future
2464 version. In the future, it would also be good to
2465 separate DB access and UI presentation field names
2471 my ( $table, $column ) = @_;
2472 if ( $column eq "cn_sort" or $column eq "cn_source" ) {
2473 return $table . '.' . $column;
2480 =head2 TransformMarcToKohaOneField
2482 $val = TransformMarcToKohaOneField( 'biblio.title', $marc );
2484 Note: The authoritative Default framework is used implicitly.
2488 sub TransformMarcToKohaOneField {
2489 my ( $kohafield, $marc ) = @_;
2491 my ( @rv, $retval );
2492 my @mss = GetMarcSubfieldStructureFromKohaField($kohafield);
2493 foreach my $fldhash ( @mss ) {
2494 my $tag = $fldhash->{tagfield};
2495 my $sub = $fldhash->{tagsubfield};
2496 foreach my $fld ( $marc->field($tag) ) {
2497 if( $sub eq '@' || $fld->is_control_field ) {
2498 push @rv, $fld->data if $fld->data;
2500 push @rv, grep { $_ } $fld->subfield($sub);
2505 $retval = join ' | ', uniq(@rv);
2507 # Additional polishing for individual kohafields
2508 if( $kohafield =~ /copyrightdate|publicationyear/ ) {
2509 $retval = _adjust_pubyear( $retval );
2515 =head2 _adjust_pubyear
2517 Helper routine for TransformMarcToKohaOneField
2521 sub _adjust_pubyear {
2523 # modify return value to keep only the 1st year found
2524 if( $retval =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
2526 } elsif( $retval =~ m/(\d\d\d\d)/ && $1 > 0 ) {
2528 } elsif( $retval =~ m/
2529 (?<year>\d)[-]?[.Xx?]{3}
2530 |(?<year>\d{2})[.Xx?]{2}
2531 |(?<year>\d{3})[.Xx?]
2532 |(?<year>\d)[-]{3}\?
2533 |(?<year>\d\d)[-]{2}\?
2534 |(?<year>\d{3})[-]\?
2535 /xms ) { # the form 198-? occurred in Dutch ISBD rules
2536 my $digits = $+{year};
2537 $retval = $digits * ( 10 ** ( 4 - length($digits) ));
2544 =head2 CountItemsIssued
2546 my $count = CountItemsIssued( $biblionumber );
2550 sub CountItemsIssued {
2551 my ($biblionumber) = @_;
2552 my $dbh = C4::Context->dbh;
2553 my $sth = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
2554 $sth->execute($biblionumber);
2555 my $row = $sth->fetchrow_hashref();
2556 return $row->{'issuedCount'};
2561 ModZebra( $record_number, $op, $server );
2563 $record_number is the authid or biblionumber we want to index
2565 $op is the operation: specialUpdate or recordDelete
2567 $server is authorityserver or biblioserver
2572 my ( $record_number, $op, $server ) = @_;
2573 $debug && warn "ModZebra: updates requested for: $record_number $op $server\n";
2574 my $dbh = C4::Context->dbh;
2576 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2578 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2579 # the table is emptied by rebuild_zebra.pl script (using the -z switch)
2580 my $check_sql = "SELECT COUNT(*) FROM zebraqueue
2582 AND biblio_auth_number = ?
2585 my $check_sth = $dbh->prepare_cached($check_sql);
2586 $check_sth->execute( $server, $record_number, $op );
2587 my ($count) = $check_sth->fetchrow_array;
2588 $check_sth->finish();
2589 if ( $count == 0 ) {
2590 my $sth = $dbh->prepare("INSERT INTO zebraqueue (biblio_auth_number,server,operation) VALUES(?,?,?)");
2591 $sth->execute( $record_number, $server, $op );
2596 =head2 EmbedItemsInMarcBiblio
2598 EmbedItemsInMarcBiblio({
2599 marc_record => $marc,
2600 biblionumber => $biblionumber,
2601 item_numbers => $itemnumbers,
2604 Given a MARC::Record object containing a bib record,
2605 modify it to include the items attached to it as 9XX
2606 per the bib's MARC framework.
2607 if $itemnumbers is defined, only specified itemnumbers are embedded.
2609 If $opac is true, then opac-relevant suppressions are included.
2611 If opac filtering will be done, borcat should be passed to properly
2612 override if necessary.
2616 sub EmbedItemsInMarcBiblio {
2618 my ($marc, $biblionumber, $itemnumbers, $opac, $borcat);
2619 $marc = $params->{marc_record};
2621 carp 'EmbedItemsInMarcBiblio: No MARC record passed';
2624 $biblionumber = $params->{biblionumber};
2625 $itemnumbers = $params->{item_numbers};
2626 $opac = $params->{opac};
2627 $borcat = $params->{borcat} // q{};
2629 $itemnumbers = [] unless defined $itemnumbers;
2631 my $frameworkcode = GetFrameworkCode($biblionumber);
2632 _strip_item_fields($marc, $frameworkcode);
2634 # ... and embed the current items
2635 my $dbh = C4::Context->dbh;
2636 my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
2637 $sth->execute($biblionumber);
2638 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
2640 my @item_fields; # Array holding the actual MARC data for items to be included.
2641 my @items; # Array holding items which are both in the list (sitenumbers)
2642 # and on this biblionumber
2644 # Flag indicating if there is potential hiding.
2645 my $opachiddenitems = $opac
2646 && ( C4::Context->preference('OpacHiddenItems') !~ /^\s*$/ );
2649 while ( my ($itemnumber) = $sth->fetchrow_array ) {
2650 next if @$itemnumbers and not grep { $_ == $itemnumber } @$itemnumbers;
2652 if ( $opachiddenitems ) {
2653 $item = Koha::Items->find($itemnumber);
2654 $item = $item ? $item->unblessed : undef;
2656 push @items, { itemnumber => $itemnumber, item => $item };
2658 my @items2pass = map { $_->{item} } @items;
2661 ? C4::Items::GetHiddenItemnumbers({
2662 items => \@items2pass,
2663 borcat => $borcat })
2665 # Convert to a hash for quick searching
2666 my %hiddenitems = map { $_ => 1 } @hiddenitems;
2667 foreach my $itemnumber ( map { $_->{itemnumber} } @items ) {
2668 next if $hiddenitems{$itemnumber};
2669 my $item_marc = C4::Items::GetMarcItem( $biblionumber, $itemnumber );
2670 push @item_fields, $item_marc->field($itemtag);
2672 $marc->append_fields(@item_fields);
2675 =head1 INTERNAL FUNCTIONS
2677 =head2 _koha_marc_update_bib_ids
2680 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
2682 Internal function to add or update biblionumber and biblioitemnumber to
2687 sub _koha_marc_update_bib_ids {
2688 my ( $record, $frameworkcode, $biblionumber, $biblioitemnumber ) = @_;
2690 my ( $biblio_tag, $biblio_subfield ) = GetMarcFromKohaField( "biblio.biblionumber" );
2691 die qq{No biblionumber tag for framework "$frameworkcode"} unless $biblio_tag;
2692 my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber" );
2693 die qq{No biblioitemnumber tag for framework "$frameworkcode"} unless $biblioitem_tag;
2695 if ( $biblio_tag < 10 ) {
2696 C4::Biblio::UpsertMarcControlField( $record, $biblio_tag, $biblionumber );
2698 C4::Biblio::UpsertMarcSubfield($record, $biblio_tag, $biblio_subfield, $biblionumber);
2700 if ( $biblioitem_tag < 10 ) {
2701 C4::Biblio::UpsertMarcControlField( $record, $biblioitem_tag, $biblioitemnumber );
2703 C4::Biblio::UpsertMarcSubfield($record, $biblioitem_tag, $biblioitem_subfield, $biblioitemnumber);
2707 =head2 _koha_marc_update_biblioitem_cn_sort
2709 _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
2711 Given a MARC bib record and the biblioitem hash, update the
2712 subfield that contains a copy of the value of biblioitems.cn_sort.
2716 sub _koha_marc_update_biblioitem_cn_sort {
2718 my $biblioitem = shift;
2719 my $frameworkcode = shift;
2721 my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.cn_sort" );
2722 return unless $biblioitem_tag;
2724 my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2726 if ( my $field = $marc->field($biblioitem_tag) ) {
2727 $field->delete_subfield( code => $biblioitem_subfield );
2728 if ( $cn_sort ne '' ) {
2729 $field->add_subfields( $biblioitem_subfield => $cn_sort );
2733 # if we get here, no biblioitem tag is present in the MARC record, so
2734 # we'll create it if $cn_sort is not empty -- this would be
2735 # an odd combination of events, however
2737 $marc->insert_grouped_field( MARC::Field->new( $biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort ) );
2742 =head2 _koha_modify_biblio
2744 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
2746 Internal function for updating the biblio table
2750 sub _koha_modify_biblio {
2751 my ( $dbh, $biblio, $frameworkcode ) = @_;
2756 SET frameworkcode = ?,
2769 WHERE biblionumber = ?
2772 my $sth = $dbh->prepare($query);
2775 $frameworkcode, $biblio->{'author'}, $biblio->{'title'}, $biblio->{'subtitle'},
2776 $biblio->{'medium'}, $biblio->{'part_number'}, $biblio->{'part_name'}, $biblio->{'unititle'},
2777 $biblio->{'notes'}, $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'} ? int($biblio->{'copyrightdate'}) : undef,
2778 $biblio->{'abstract'}, $biblio->{'biblionumber'}
2779 ) if $biblio->{'biblionumber'};
2781 if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
2782 $error .= "ERROR in _koha_modify_biblio $query" . $dbh->errstr;
2785 return ( $biblio->{'biblionumber'}, $error );
2788 =head2 _koha_modify_biblioitem_nonmarc
2790 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
2794 sub _koha_modify_biblioitem_nonmarc {
2795 my ( $dbh, $biblioitem ) = @_;
2798 # re-calculate the cn_sort, it may have changed
2799 my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2801 my $query = "UPDATE biblioitems
2802 SET biblionumber = ?,
2808 publicationyear = ?,
2812 collectiontitle = ?,
2814 collectionvolume= ?,
2815 editionstatement= ?,
2816 editionresponsibility = ?,
2832 where biblioitemnumber = ?
2834 my $sth = $dbh->prepare($query);
2836 $biblioitem->{'biblionumber'}, $biblioitem->{'volume'}, $biblioitem->{'number'}, $biblioitem->{'itemtype'},
2837 $biblioitem->{'isbn'}, $biblioitem->{'issn'}, $biblioitem->{'publicationyear'}, $biblioitem->{'publishercode'},
2838 $biblioitem->{'volumedate'}, $biblioitem->{'volumedesc'}, $biblioitem->{'collectiontitle'}, $biblioitem->{'collectionissn'},
2839 $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
2840 $biblioitem->{'pages'}, $biblioitem->{'bnotes'}, $biblioitem->{'size'}, $biblioitem->{'place'},
2841 $biblioitem->{'lccn'}, $biblioitem->{'url'}, $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'},
2842 $biblioitem->{'cn_item'}, $biblioitem->{'cn_suffix'}, $cn_sort, $biblioitem->{'totalissues'},
2843 $biblioitem->{'ean'}, $biblioitem->{'agerestriction'}, $biblioitem->{'biblioitemnumber'}
2845 if ( $dbh->errstr ) {
2846 $error .= "ERROR in _koha_modify_biblioitem_nonmarc $query" . $dbh->errstr;
2849 return ( $biblioitem->{'biblioitemnumber'}, $error );
2852 =head2 _koha_delete_biblio
2854 $error = _koha_delete_biblio($dbh,$biblionumber);
2856 Internal sub for deleting from biblio table -- also saves to deletedbiblio
2858 C<$dbh> - the database handle
2860 C<$biblionumber> - the biblionumber of the biblio to be deleted
2864 # FIXME: add error handling
2866 sub _koha_delete_biblio {
2867 my ( $dbh, $biblionumber ) = @_;
2869 # get all the data for this biblio
2870 my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
2871 $sth->execute($biblionumber);
2873 # FIXME There is a transaction in _koha_delete_biblio_metadata
2874 # But actually all the following should be done inside a single transaction
2875 if ( my $data = $sth->fetchrow_hashref ) {
2877 # save the record in deletedbiblio
2878 # find the fields to save
2879 my $query = "INSERT INTO deletedbiblio SET ";
2881 foreach my $temp ( keys %$data ) {
2882 $query .= "$temp = ?,";
2883 push( @bind, $data->{$temp} );
2886 # replace the last , by ",?)"
2888 my $bkup_sth = $dbh->prepare($query);
2889 $bkup_sth->execute(@bind);
2892 _koha_delete_biblio_metadata( $biblionumber );
2895 my $sth2 = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
2896 $sth2->execute($biblionumber);
2897 # update the timestamp (Bugzilla 7146)
2898 $sth2= $dbh->prepare("UPDATE deletedbiblio SET timestamp=NOW() WHERE biblionumber=?");
2899 $sth2->execute($biblionumber);
2906 =head2 _koha_delete_biblioitems
2908 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
2910 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
2912 C<$dbh> - the database handle
2913 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
2917 # FIXME: add error handling
2919 sub _koha_delete_biblioitems {
2920 my ( $dbh, $biblioitemnumber ) = @_;
2922 # get all the data for this biblioitem
2923 my $sth = $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
2924 $sth->execute($biblioitemnumber);
2926 if ( my $data = $sth->fetchrow_hashref ) {
2928 # save the record in deletedbiblioitems
2929 # find the fields to save
2930 my $query = "INSERT INTO deletedbiblioitems SET ";
2932 foreach my $temp ( keys %$data ) {
2933 $query .= "$temp = ?,";
2934 push( @bind, $data->{$temp} );
2937 # replace the last , by ",?)"
2939 my $bkup_sth = $dbh->prepare($query);
2940 $bkup_sth->execute(@bind);
2943 # delete the biblioitem
2944 my $sth2 = $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
2945 $sth2->execute($biblioitemnumber);
2946 # update the timestamp (Bugzilla 7146)
2947 $sth2= $dbh->prepare("UPDATE deletedbiblioitems SET timestamp=NOW() WHERE biblioitemnumber=?");
2948 $sth2->execute($biblioitemnumber);
2955 =head2 _koha_delete_biblio_metadata
2957 $error = _koha_delete_biblio_metadata($biblionumber);
2959 C<$biblionumber> - the biblionumber of the biblio metadata to be deleted
2963 sub _koha_delete_biblio_metadata {
2964 my ($biblionumber) = @_;
2966 my $dbh = C4::Context->dbh;
2967 my $schema = Koha::Database->new->schema;
2971 INSERT INTO deletedbiblio_metadata (biblionumber, format, `schema`, metadata)
2972 SELECT biblionumber, format, `schema`, metadata FROM biblio_metadata WHERE biblionumber=?
2973 |, undef, $biblionumber );
2974 $dbh->do( q|DELETE FROM biblio_metadata WHERE biblionumber=?|,
2975 undef, $biblionumber );
2980 =head1 UNEXPORTED FUNCTIONS
2982 =head2 ModBiblioMarc
2984 &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
2986 Add MARC XML data for a biblio to koha
2988 Function exported, but should NOT be used, unless you really know what you're doing
2993 # pass the MARC::Record to this function, and it will create the records in
2995 my ( $record, $biblionumber, $frameworkcode ) = @_;
2997 carp 'ModBiblioMarc passed an undefined record';
3001 # Clone record as it gets modified
3002 $record = $record->clone();
3003 my $dbh = C4::Context->dbh;
3004 my @fields = $record->fields();
3005 if ( !$frameworkcode ) {
3006 $frameworkcode = "";
3008 my $sth = $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3009 $sth->execute( $frameworkcode, $biblionumber );
3011 my $encoding = C4::Context->preference("marcflavour");
3013 # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3014 if ( $encoding eq "UNIMARC" ) {
3015 my $defaultlanguage = C4::Context->preference("UNIMARCField100Language");
3016 $defaultlanguage = "fre" if (!$defaultlanguage || length($defaultlanguage) != 3);
3017 my $string = $record->subfield( 100, "a" );
3018 if ( ($string) && ( length( $record->subfield( 100, "a" ) ) == 36 ) ) {
3019 my $f100 = $record->field(100);
3020 $record->delete_field($f100);
3022 $string = POSIX::strftime( "%Y%m%d", localtime );
3024 $string = sprintf( "%-*s", 35, $string );
3025 substr ( $string, 22, 3, $defaultlanguage);
3027 substr( $string, 25, 3, "y50" );
3028 unless ( $record->subfield( 100, "a" ) ) {
3029 $record->insert_fields_ordered( MARC::Field->new( 100, "", "", "a" => $string ) );
3033 #enhancement 5374: update transaction date (005) for marc21/unimarc
3034 if($encoding =~ /MARC21|UNIMARC/) {
3035 my @a= (localtime) [5,4,3,2,1,0]; $a[0]+=1900; $a[1]++;
3036 # YY MM DD HH MM SS (update year and month)
3037 my $f005= $record->field('005');
3038 $f005->update(sprintf("%4d%02d%02d%02d%02d%04.1f",@a)) if $f005;
3042 biblionumber => $biblionumber,
3043 format => 'marcxml',
3044 schema => C4::Context->preference('marcflavour'),
3046 $record->as_usmarc; # Bug 20126/10455 This triggers field length calculation
3048 my $m_rs = Koha::Biblio::Metadatas->find($metadata) //
3049 Koha::Biblio::Metadata->new($metadata);
3051 my $userenv = C4::Context->userenv;
3053 my $borrowernumber = $userenv->{number};
3054 my $borrowername = join ' ', map { $_ // q{} } @$userenv{qw(firstname surname)};
3055 unless ($m_rs->in_storage) {
3056 Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForCreatorId'), $borrowernumber);
3057 Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForCreatorName'), $borrowername);
3059 Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForModifierId'), $borrowernumber);
3060 Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForModifierName'), $borrowername);
3063 $m_rs->metadata( $record->as_xml_record($encoding) );
3066 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
3067 $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
3069 return $biblionumber;
3072 =head2 prepare_host_field
3074 $marcfield = prepare_host_field( $hostbiblioitem, $marcflavour );
3075 Generate the host item entry for an analytic child entry
3079 sub prepare_host_field {
3080 my ( $hostbiblio, $marcflavour ) = @_;
3081 $marcflavour ||= C4::Context->preference('marcflavour');
3082 my $host = GetMarcBiblio({ biblionumber => $hostbiblio });
3083 # unfortunately as_string does not 'do the right thing'
3084 # if field returns undef
3088 if ( $marcflavour eq 'MARC21' || $marcflavour eq 'NORMARC' ) {
3089 if ( $field = $host->field('100') || $host->field('110') || $host->field('11') ) {
3090 my $s = $field->as_string('ab');
3095 if ( $field = $host->field('245') ) {
3096 my $s = $field->as_string('a');
3101 if ( $field = $host->field('260') ) {
3102 my $s = $field->as_string('abc');
3107 if ( $field = $host->field('240') ) {
3108 my $s = $field->as_string();
3113 if ( $field = $host->field('022') ) {
3114 my $s = $field->as_string('a');
3119 if ( $field = $host->field('020') ) {
3120 my $s = $field->as_string('a');
3125 if ( $field = $host->field('001') ) {
3126 $sfd{w} = $field->data(),;
3128 $host_field = MARC::Field->new( 773, '0', ' ', %sfd );
3131 elsif ( $marcflavour eq 'UNIMARC' ) {
3133 if ( $field = $host->field('700') || $host->field('710') || $host->field('720') ) {
3134 my $s = $field->as_string('ab');
3140 if ( $field = $host->field('200') ) {
3141 my $s = $field->as_string('a');
3146 #place of publicaton
3147 if ( $field = $host->field('210') ) {
3148 my $s = $field->as_string('a');
3153 #date of publication
3154 if ( $field = $host->field('210') ) {
3155 my $s = $field->as_string('d');
3161 if ( $field = $host->field('205') ) {
3162 my $s = $field->as_string();
3168 if ( $field = $host->field('856') ) {
3169 my $s = $field->as_string('u');
3175 if ( $field = $host->field('011') ) {
3176 my $s = $field->as_string('a');
3182 if ( $field = $host->field('010') ) {
3183 my $s = $field->as_string('a');
3188 if ( $field = $host->field('001') ) {
3189 $sfd{0} = $field->data(),;
3191 $host_field = MARC::Field->new( 461, '0', ' ', %sfd );
3198 =head2 UpdateTotalIssues
3200 UpdateTotalIssues($biblionumber, $increase, [$value])
3202 Update the total issue count for a particular bib record.
3206 =item C<$biblionumber> is the biblionumber of the bib to update
3208 =item C<$increase> is the amount to increase (or decrease) the total issues count by
3210 =item C<$value> is the absolute value that total issues count should be set to. If provided, C<$increase> is ignored.
3216 sub UpdateTotalIssues {
3217 my ($biblionumber, $increase, $value) = @_;
3220 my $record = GetMarcBiblio({ biblionumber => $biblionumber });
3222 carp "UpdateTotalIssues could not get biblio record";
3225 my $biblio = Koha::Biblios->find( $biblionumber );
3227 carp "UpdateTotalIssues could not get datas of biblio";
3230 my $biblioitem = $biblio->biblioitem;
3231 my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField( 'biblioitems.totalissues' );
3232 unless ($totalissuestag) {
3233 return 1; # There is nothing to do
3236 if (defined $value) {
3237 $totalissues = $value;
3239 $totalissues = $biblioitem->totalissues + $increase;
3242 my $field = $record->field($totalissuestag);
3243 if (defined $field) {
3244 $field->update( $totalissuessubfield => $totalissues );
3246 $field = MARC::Field->new($totalissuestag, '0', '0',
3247 $totalissuessubfield => $totalissues);
3248 $record->insert_grouped_field($field);
3251 return ModBiblio($record, $biblionumber, $biblio->frameworkcode);
3256 &RemoveAllNsb($record);
3258 Removes all nsb/nse chars from a record
3265 carp 'RemoveAllNsb called with undefined record';
3269 SetUTF8Flag($record);
3271 foreach my $field ($record->fields()) {
3272 if ($field->is_control_field()) {
3273 $field->update(nsb_clean($field->data()));
3275 my @subfields = $field->subfields();
3277 foreach my $subfield (@subfields) {
3278 push @new_subfields, $subfield->[0] => nsb_clean($subfield->[1]);
3280 if (scalar(@new_subfields) > 0) {
3283 $new_field = MARC::Field->new(
3285 $field->indicator(1),
3286 $field->indicator(2),
3291 warn "error in RemoveAllNsb : $@";
3293 $field->replace_with($new_field);
3305 =head2 _after_biblio_action_hooks
3307 Helper method that takes care of calling all plugin hooks
3311 sub _after_biblio_action_hooks {
3314 my $biblio_id = $args->{biblio_id};
3315 my $action = $args->{action};
3317 my $biblio = Koha::Biblios->find( $biblio_id );
3318 Koha::Plugins->call(
3319 'after_biblio_action',
3323 biblio_id => $biblio_id,
3332 Koha Development Team <http://koha-community.org/>
3334 Paul POULAIN paul.poulain@free.fr
3336 Joshua Ferraro jmf@liblime.com