Bug 29486: _koha_marc_update_bib_ids no longer needed for GetMarcBiblio
[koha.git] / C4 / Biblio.pm
1 package C4::Biblio;
2
3 # Copyright 2000-2002 Katipo Communications
4 # Copyright 2010 BibLibre
5 # Copyright 2011 Equinox Software, Inc.
6 #
7 # This file is part of Koha.
8 #
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.
13 #
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.
18 #
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>.
21
22 use Modern::Perl;
23
24 use vars qw(@ISA @EXPORT_OK);
25 BEGIN {
26     require Exporter;
27     @ISA = qw(Exporter);
28
29     @EXPORT_OK = qw(
30         AddBiblio
31         GetBiblioData
32         GetMarcBiblio
33         GetISBDView
34         GetMarcControlnumber
35         GetMarcISBN
36         GetMarcISSN
37         GetMarcSubjects
38         GetMarcSeries
39         GetMarcUrls
40         GetUsedMarcStructure
41         GetXmlBiblio
42         GetMarcPrice
43         MungeMarcPrice
44         GetMarcQuantity
45         GetAuthorisedValueDesc
46         GetMarcStructure
47         GetMarcSubfieldStructure
48         IsMarcStructureInternal
49         GetMarcFromKohaField
50         GetMarcSubfieldStructureFromKohaField
51         GetFrameworkCode
52         TransformKohaToMarc
53         PrepHostMarcField
54         CountItemsIssued
55         ModBiblio
56         ModZebra
57         EmbedItemsInMarcBiblio
58         UpdateTotalIssues
59         RemoveAllNsb
60         DelBiblio
61         BiblioAutoLink
62         LinkBibHeadingsToAuthorities
63         ApplyMarcOverlayRules
64         TransformMarcToKoha
65         TransformHtmlToMarc
66         TransformHtmlToXml
67         prepare_host_field
68         TransformMarcToKohaOneField
69     );
70
71     # Internal functions
72     # those functions are exported but should not be used
73     # they are useful in a few circumstances, so they are exported,
74     # but don't use them unless you are a core developer ;-)
75     push @EXPORT_OK, qw(
76       ModBiblioMarc
77     );
78 }
79
80 use Carp qw( carp );
81 use Try::Tiny qw( catch try );
82
83 use Encode;
84 use List::MoreUtils qw( uniq );
85 use MARC::Record;
86 use MARC::File::USMARC;
87 use MARC::File::XML;
88 use POSIX qw( strftime );
89 use Module::Load::Conditional qw( can_load );
90
91 use C4::Koha;
92 use C4::Log qw( logaction );    # logaction
93 use C4::Budgets;
94 use C4::ClassSource qw( GetClassSort GetClassSource );
95 use C4::Charset qw(
96     nsb_clean
97     SetMarcUnicodeFlag
98     SetUTF8Flag
99     StripNonXmlChars
100 );
101 use C4::Linker;
102 use C4::OAI::Sets;
103 use C4::Items qw( GetHiddenItemnumbers GetMarcItem );
104
105 use Koha::Logger;
106 use Koha::Caches;
107 use Koha::Authority::Types;
108 use Koha::Acquisition::Currencies;
109 use Koha::Biblio::Metadatas;
110 use Koha::Holds;
111 use Koha::ItemTypes;
112 use Koha::MarcOverlayRules;
113 use Koha::Plugins;
114 use Koha::SearchEngine;
115 use Koha::SearchEngine::Indexer;
116 use Koha::Libraries;
117 use Koha::Util::MARC;
118
119 =head1 NAME
120
121 C4::Biblio - cataloging management functions
122
123 =head1 DESCRIPTION
124
125 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:
126
127 =over 4
128
129 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
130
131 =item 2. as raw MARC in the Zebra index and storage engine
132
133 =item 3. as MARC XML in biblio_metadata.metadata
134
135 =back
136
137 In the 3.0 version of Koha, the authoritative record-level information is in biblio_metadata.metadata
138
139 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.
140
141 =over 4
142
143 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
144
145 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
146
147 =back
148
149 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:
150
151 =over 4
152
153 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
154
155 =item 2. _koha_* - low-level internal functions for managing the koha tables
156
157 =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.
158
159 =item 4. Zebra functions used to update the Zebra index
160
161 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
162
163 =back
164
165 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 :
166
167 =over 4
168
169 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
170
171 =item 2. add the biblionumber and biblioitemnumber into the MARC records
172
173 =item 3. save the marc record
174
175 =back
176
177 =head1 EXPORTED FUNCTIONS
178
179 =head2 AddBiblio
180
181   ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
182
183 Exported function (core API) for adding a new biblio to koha.
184
185 The first argument is a C<MARC::Record> object containing the
186 bib to add, while the second argument is the desired MARC
187 framework code.
188
189 This function also accepts a third, optional argument: a hashref
190 to additional options.  The only defined option is C<defer_marc_save>,
191 which if present and mapped to a true value, causes C<AddBiblio>
192 to omit the call to save the MARC in C<biblio_metadata.metadata>
193 This option is provided B<only>
194 for the use of scripts such as C<bulkmarcimport.pl> that may need
195 to do some manipulation of the MARC record for item parsing before
196 saving it and which cannot afford the performance hit of saving
197 the MARC record twice.  Consequently, do not use that option
198 unless you can guarantee that C<ModBiblioMarc> will be called.
199
200 =cut
201
202 sub AddBiblio {
203     my $record          = shift;
204     my $frameworkcode   = shift;
205     my $options         = @_ ? shift : undef;
206     my $defer_marc_save = 0;
207     if (!$record) {
208         carp('AddBiblio called with undefined record');
209         return;
210     }
211     if ( defined $options and exists $options->{'defer_marc_save'} and $options->{'defer_marc_save'} ) {
212         $defer_marc_save = 1;
213     }
214
215     my $schema = Koha::Database->schema;
216     my ( $biblionumber, $biblioitemnumber );
217     try {
218         $schema->txn_do(sub {
219
220             # transform the data into koha-table style data
221             SetUTF8Flag($record);
222             my $olddata = TransformMarcToKoha( $record, $frameworkcode );
223
224             my $biblio = Koha::Biblio->new(
225                 {
226                     frameworkcode => $frameworkcode,
227                     author        => $olddata->{author},
228                     title         => $olddata->{title},
229                     subtitle      => $olddata->{subtitle},
230                     medium        => $olddata->{medium},
231                     part_number   => $olddata->{part_number},
232                     part_name     => $olddata->{part_name},
233                     unititle      => $olddata->{unititle},
234                     notes         => $olddata->{notes},
235                     serial =>
236                       ( $olddata->{serial} || $olddata->{seriestitle} ? 1 : 0 ),
237                     seriestitle   => $olddata->{seriestitle},
238                     copyrightdate => $olddata->{copyrightdate},
239                     datecreated   => \'NOW()',
240                     abstract      => $olddata->{abstract},
241                 }
242             )->store;
243             $biblionumber = $biblio->biblionumber;
244             Koha::Exceptions::ObjectNotCreated->throw unless $biblio;
245
246             my ($cn_sort) = GetClassSort( $olddata->{'biblioitems.cn_source'}, $olddata->{'cn_class'}, $olddata->{'cn_item'} );
247             my $biblioitem = Koha::Biblioitem->new(
248                 {
249                     biblionumber          => $biblionumber,
250                     volume                => $olddata->{volume},
251                     number                => $olddata->{number},
252                     itemtype              => $olddata->{itemtype},
253                     isbn                  => $olddata->{isbn},
254                     issn                  => $olddata->{issn},
255                     publicationyear       => $olddata->{publicationyear},
256                     publishercode         => $olddata->{publishercode},
257                     volumedate            => $olddata->{volumedate},
258                     volumedesc            => $olddata->{volumedesc},
259                     collectiontitle       => $olddata->{collectiontitle},
260                     collectionissn        => $olddata->{collectionissn},
261                     collectionvolume      => $olddata->{collectionvolume},
262                     editionstatement      => $olddata->{editionstatement},
263                     editionresponsibility => $olddata->{editionresponsibility},
264                     illus                 => $olddata->{illus},
265                     pages                 => $olddata->{pages},
266                     notes                 => $olddata->{bnotes},
267                     size                  => $olddata->{size},
268                     place                 => $olddata->{place},
269                     lccn                  => $olddata->{lccn},
270                     url                   => $olddata->{url},
271                     cn_source      => $olddata->{'biblioitems.cn_source'},
272                     cn_class       => $olddata->{cn_class},
273                     cn_item        => $olddata->{cn_item},
274                     cn_suffix      => $olddata->{cn_suff},
275                     cn_sort        => $cn_sort,
276                     totalissues    => $olddata->{totalissues},
277                     ean            => $olddata->{ean},
278                     agerestriction => $olddata->{agerestriction},
279                 }
280             )->store;
281             Koha::Exceptions::ObjectNotCreated->throw unless $biblioitem;
282             $biblioitemnumber = $biblioitem->biblioitemnumber;
283
284             _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber );
285
286             # update MARC subfield that stores biblioitems.cn_sort
287             _koha_marc_update_biblioitem_cn_sort( $record, $olddata, $frameworkcode );
288
289             if (C4::Context->preference('BiblioAddsAuthorities')) {
290                 BiblioAutoLink( $record, $frameworkcode );
291             }
292
293             # now add the record
294             ModBiblioMarc( $record, $biblionumber ) unless $defer_marc_save;
295
296             # update OAI-PMH sets
297             if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
298                 C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
299             }
300
301             _after_biblio_action_hooks({ action => 'create', biblio_id => $biblionumber });
302
303             logaction( "CATALOGUING", "ADD", $biblionumber, "biblio" ) if C4::Context->preference("CataloguingLog");
304         });
305     } catch {
306         warn $_;
307         ( $biblionumber, $biblioitemnumber ) = ( undef, undef );
308     };
309     return ( $biblionumber, $biblioitemnumber );
310 }
311
312 =head2 ModBiblio
313
314   ModBiblio($record, $biblionumber, $frameworkcode, $options);
315
316 Replace an existing bib record identified by C<$biblionumber>
317 with one supplied by the MARC::Record object C<$record>.  The embedded
318 item, biblioitem, and biblionumber fields from the previous
319 version of the bib record replace any such fields of those tags that
320 are present in C<$record>.  Consequently, ModBiblio() is not
321 to be used to try to modify item records.
322
323 C<$frameworkcode> specifies the MARC framework to use
324 when storing the modified bib record; among other things,
325 this controls how MARC fields get mapped to display columns
326 in the C<biblio> and C<biblioitems> tables, as well as
327 which fields are used to store embedded item, biblioitem,
328 and biblionumber data for indexing.
329
330 The C<$options> argument is a hashref with additional parameters:
331
332 =over 4
333
334 =item C<overlay_context>
335
336 This parameter is forwarded to L</ApplyMarcOverlayRules> where it is used for
337 selecting the current rule set if MARCOverlayRules is enabled.
338 See L</ApplyMarcOverlayRules> for more details.
339
340 =item C<disable_autolink>
341
342 Unless C<disable_autolink> is passed ModBiblio will relink record headings
343 to authorities based on settings in the system preferences. This flag allows
344 us to not relink records when the authority linker is saving modifications.
345
346 =back
347
348 Returns 1 on success 0 on failure
349
350 =cut
351
352 sub ModBiblio {
353     my ( $record, $biblionumber, $frameworkcode, $options ) = @_;
354     $options //= {};
355
356     if (!$record) {
357         carp 'No record passed to ModBiblio';
358         return 0;
359     }
360
361     if ( C4::Context->preference("CataloguingLog") ) {
362         my $newrecord = GetMarcBiblio({ biblionumber => $biblionumber });
363         logaction( "CATALOGUING", "MODIFY", $biblionumber, "biblio BEFORE=>" . $newrecord->as_formatted );
364     }
365
366     if ( !$options->{disable_autolink} && C4::Context->preference('BiblioAddsAuthorities') ) {
367         BiblioAutoLink( $record, $frameworkcode );
368     }
369
370     # Cleaning up invalid fields must be done early or SetUTF8Flag is liable to
371     # throw an exception which probably won't be handled.
372     foreach my $field ($record->fields()) {
373         if (! $field->is_control_field()) {
374             if (scalar($field->subfields()) == 0 || (scalar($field->subfields()) == 1 && $field->subfield('9'))) {
375                 $record->delete_field($field);
376             }
377         }
378     }
379
380     SetUTF8Flag($record);
381     my $dbh = C4::Context->dbh;
382
383     $frameworkcode = "" if !$frameworkcode || $frameworkcode eq "Default"; # XXX
384
385     _strip_item_fields($record, $frameworkcode);
386
387     # apply overlay rules
388     if (   C4::Context->preference('MARCOverlayRules')
389         && $biblionumber
390         && defined $options
391         && exists $options->{overlay_context} )
392     {
393         $record = ApplyMarcOverlayRules(
394             {
395                 biblionumber    => $biblionumber,
396                 record          => $record,
397                 overlay_context => $options->{overlay_context},
398             }
399         );
400     }
401
402     # update biblionumber and biblioitemnumber in MARC
403     # FIXME - this is assuming a 1 to 1 relationship between
404     # biblios and biblioitems
405     my $sth = $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
406     $sth->execute($biblionumber);
407     my ($biblioitemnumber) = $sth->fetchrow;
408     $sth->finish();
409     _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber );
410
411     # load the koha-table data object
412     my $oldbiblio = TransformMarcToKoha( $record, $frameworkcode );
413
414     # update MARC subfield that stores biblioitems.cn_sort
415     _koha_marc_update_biblioitem_cn_sort( $record, $oldbiblio, $frameworkcode );
416
417     # update the MARC record (that now contains biblio and items) with the new record data
418     ModBiblioMarc( $record, $biblionumber );
419
420     # modify the other koha tables
421     _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
422     _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
423
424     _after_biblio_action_hooks({ action => 'modify', biblio_id => $biblionumber });
425
426     # update OAI-PMH sets
427     if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
428         C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
429     }
430
431     return 1;
432 }
433
434 =head2 _strip_item_fields
435
436   _strip_item_fields($record, $frameworkcode)
437
438 Utility routine to remove item tags from a
439 MARC bib.
440
441 =cut
442
443 sub _strip_item_fields {
444     my $record = shift;
445     my $frameworkcode = shift;
446     # get the items before and append them to the biblio before updating the record, atm we just have the biblio
447     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
448
449     # delete any item fields from incoming record to avoid
450     # duplication or incorrect data - use AddItem() or ModItem()
451     # to change items
452     foreach my $field ( $record->field($itemtag) ) {
453         $record->delete_field($field);
454     }
455 }
456
457 =head2 DelBiblio
458
459   my $error = &DelBiblio($biblionumber);
460
461 Exported function (core API) for deleting a biblio in koha.
462 Deletes biblio record from Zebra and Koha tables (biblio & biblioitems)
463 Also backs it up to deleted* tables.
464 Checks to make sure that the biblio has no items attached.
465 return:
466 C<$error> : undef unless an error occurs
467
468 =cut
469
470 sub DelBiblio {
471     my ($biblionumber, $params) = @_;
472
473     my $biblio = Koha::Biblios->find( $biblionumber );
474     return unless $biblio; # Should we throw an exception instead?
475
476     my $dbh = C4::Context->dbh;
477     my $error;    # for error handling
478
479     # First make sure this biblio has no items attached
480     my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
481     $sth->execute($biblionumber);
482     if ( my $itemnumber = $sth->fetchrow ) {
483
484         # Fix this to use a status the template can understand
485         $error .= "This Biblio has items attached, please delete them first before deleting this biblio ";
486     }
487
488     return $error if $error;
489
490     # We delete any existing holds
491     my $holds = $biblio->holds;
492     while ( my $hold = $holds->next ) {
493         $hold->cancel;
494     }
495
496     unless ( $params->{skip_record_index} ){
497         my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
498         $indexer->index_records( $biblionumber, "recordDelete", "biblioserver" );
499     }
500
501     # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
502     $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
503     $sth->execute($biblionumber);
504     while ( my $biblioitemnumber = $sth->fetchrow ) {
505
506         # delete this biblioitem
507         $error = _koha_delete_biblioitems( $dbh, $biblioitemnumber );
508         return $error if $error;
509     }
510
511
512     # delete biblio from Koha tables and save in deletedbiblio
513     # must do this *after* _koha_delete_biblioitems, otherwise
514     # delete cascade will prevent deletedbiblioitems rows
515     # from being generated by _koha_delete_biblioitems
516     $error = _koha_delete_biblio( $dbh, $biblionumber );
517
518     _after_biblio_action_hooks({ action => 'delete', biblio_id => $biblionumber });
519
520     logaction( "CATALOGUING", "DELETE", $biblionumber, "biblio" ) if C4::Context->preference("CataloguingLog");
521
522     return;
523 }
524
525
526 =head2 BiblioAutoLink
527
528   my $headings_linked = BiblioAutoLink($record, $frameworkcode)
529
530 Automatically links headings in a bib record to authorities.
531
532 Returns the number of headings changed
533
534 =cut
535
536 sub BiblioAutoLink {
537     my $record        = shift;
538     my $frameworkcode = shift;
539     my $verbose = shift;
540     if (!$record) {
541         carp('Undefined record passed to BiblioAutoLink');
542         return 0;
543     }
544     my ( $num_headings_changed, %results );
545
546     my $linker_module =
547       "C4::Linker::" . ( C4::Context->preference("LinkerModule") || 'Default' );
548     unless ( can_load( modules => { $linker_module => undef } ) ) {
549         $linker_module = 'C4::Linker::Default';
550         unless ( can_load( modules => { $linker_module => undef } ) ) {
551             return 0;
552         }
553     }
554
555     my $linker = $linker_module->new(
556         { 'options' => C4::Context->preference("LinkerOptions") } );
557     my ( $headings_changed, $results ) =
558       LinkBibHeadingsToAuthorities( $linker, $record, $frameworkcode, C4::Context->preference("CatalogModuleRelink") || '', undef, $verbose );
559     # By default we probably don't want to relink things when cataloging
560     return $headings_changed, $results;
561 }
562
563 =head2 LinkBibHeadingsToAuthorities
564
565   my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode, [$allowrelink, $tagtolink,  $verbose]);
566
567 Links bib headings to authority records by checking
568 each authority-controlled field in the C<MARC::Record>
569 object C<$marc>, looking for a matching authority record,
570 and setting the linking subfield $9 to the ID of that
571 authority record.  
572
573 If $allowrelink is false, existing authids will never be
574 replaced, regardless of the values of LinkerKeepStale and
575 LinkerRelink.
576
577 Returns the number of heading links changed in the
578 MARC record.
579
580 =cut
581
582 sub LinkBibHeadingsToAuthorities {
583     my $linker        = shift;
584     my $bib           = shift;
585     my $frameworkcode = shift;
586     my $allowrelink = shift;
587     my $tagtolink     = shift;
588     my $verbose = shift;
589     my %results;
590     if (!$bib) {
591         carp 'LinkBibHeadingsToAuthorities called on undefined bib record';
592         return ( 0, {});
593     }
594     require C4::Heading;
595     require C4::AuthoritiesMarc;
596
597     $allowrelink = 1 unless defined $allowrelink;
598     my $num_headings_changed = 0;
599     foreach my $field ( $bib->fields() ) {
600         if ( defined $tagtolink ) {
601           next unless $field->tag() == $tagtolink ;
602         }
603         my $heading = C4::Heading->new_from_field( $field, $frameworkcode );
604         next unless defined $heading;
605
606         # check existing $9
607         my $current_link = $field->subfield('9');
608
609         if ( defined $current_link && (!$allowrelink || !C4::Context->preference('LinkerRelink')) )
610         {
611             $results{'linked'}->{ $heading->display_form() }++;
612             push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
613             next;
614         }
615
616         my ( $authid, $fuzzy, $match_count ) = $linker->get_link($heading);
617         if ($authid) {
618             $results{ $fuzzy ? 'fuzzy' : 'linked' }
619               ->{ $heading->display_form() }++;
620             if(defined $current_link and $current_link == $authid) {
621                 push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
622                 next;
623             }
624
625             $field->delete_subfield( code => '9' ) if defined $current_link;
626             $field->add_subfields( '9', $authid );
627             $num_headings_changed++;
628             push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => 'LOCAL_FOUND'}) if $verbose;
629         }
630         else {
631             my $authority_type = Koha::Authority::Types->find( $heading->auth_type() );
632             if ( defined $current_link
633                 && (!$allowrelink || C4::Context->preference('LinkerKeepStale')) )
634             {
635                 $results{'fuzzy'}->{ $heading->display_form() }++;
636                 push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
637             }
638             elsif ( C4::Context->preference('AutoCreateAuthorities') ) {
639                 if ( _check_valid_auth_link( $current_link, $field ) ) {
640                     $results{'linked'}->{ $heading->display_form() }++;
641                 }
642                 elsif ( !$match_count ) {
643                     my $authority_type = Koha::Authority::Types->find( $heading->auth_type() );
644                     my $marcrecordauth = MARC::Record->new();
645                     if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
646                         $marcrecordauth->leader('     nz  a22     o  4500');
647                         SetMarcUnicodeFlag( $marcrecordauth, 'MARC21' );
648                     }
649                     $field->delete_subfield( code => '9' )
650                       if defined $current_link;
651                     my @auth_subfields;
652                     foreach my $subfield ( $field->subfields() ){
653                         if ( $subfield->[0] =~ /[A-z]/
654                             && C4::Heading::valid_heading_subfield(
655                                 $field->tag, $subfield->[0] )
656                            ){
657                             push @auth_subfields, $subfield->[0] => $subfield->[1];
658                         }
659                     }
660                     # Bib headings contain some ending punctuation that should NOT
661                     # be included in the authority record. Strip those before creation
662                     next unless @auth_subfields; # Don't try to create a record if we have no fields;
663                     my $last_sub = pop @auth_subfields;
664                     $last_sub =~ s/[\s]*[,.:=;!%\/][\s]*$//;
665                     push @auth_subfields, $last_sub;
666                     my $authfield = MARC::Field->new( $authority_type->auth_tag_to_report, '', '', @auth_subfields );
667                     $marcrecordauth->insert_fields_ordered($authfield);
668
669 # bug 2317: ensure new authority knows it's using UTF-8; currently
670 # only need to do this for MARC21, as MARC::Record->as_xml_record() handles
671 # automatically for UNIMARC (by not transcoding)
672 # FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record
673 # use UTF-8, but as of 2008-08-05, did not want to introduce that kind
674 # of change to a core API just before the 3.0 release.
675
676                     if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
677                         my $userenv = C4::Context->userenv;
678                         my $library;
679                         if ( $userenv && $userenv->{'branch'} ) {
680                             $library = Koha::Libraries->find( $userenv->{'branch'} );
681                         }
682                         $marcrecordauth->insert_fields_ordered(
683                             MARC::Field->new(
684                                 '667', '', '',
685                                 'a' => "Machine generated authority record."
686                             )
687                         );
688                         my $cite =
689                             $bib->author() . ", "
690                           . $bib->title_proper() . ", "
691                           . $bib->publication_date() . " ";
692                         $cite =~ s/^[\s\,]*//;
693                         $cite =~ s/[\s\,]*$//;
694                         $cite =
695                             "Work cat.: ("
696                           . ( $library ? $library->get_effective_marcorgcode : C4::Context->preference('MARCOrgCode') ) . ")"
697                           . $bib->subfield( '999', 'c' ) . ": "
698                           . $cite;
699                         $marcrecordauth->insert_fields_ordered(
700                             MARC::Field->new( '670', '', '', 'a' => $cite ) );
701                     }
702
703            #          warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted;
704
705                     $authid =
706                       C4::AuthoritiesMarc::AddAuthority( $marcrecordauth, '',
707                         $heading->auth_type() );
708                     $field->add_subfields( '9', $authid );
709                     $num_headings_changed++;
710                     $linker->update_cache($heading, $authid);
711                     $results{'added'}->{ $heading->display_form() }++;
712                     push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => 'CREATED'}) if $verbose;
713                 }
714             }
715             elsif ( defined $current_link ) {
716                 if ( _check_valid_auth_link( $current_link, $field ) ) {
717                     $results{'linked'}->{ $heading->display_form() }++;
718                     push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => 'UNCHANGED'}) if $verbose;
719                 }
720                 else {
721                     $field->delete_subfield( code => '9' );
722                     $num_headings_changed++;
723                     $results{'unlinked'}->{ $heading->display_form() }++;
724                     push(@{$results{'details'}}, { tag => $field->tag(), authid => undef, status => 'NONE_FOUND', auth_type => $heading->auth_type(), tag_to_report => $authority_type->auth_tag_to_report}) if $verbose;
725                 }
726             }
727             else {
728                 $results{'unlinked'}->{ $heading->display_form() }++;
729                 push(@{$results{'details'}}, { tag => $field->tag(), authid => undef, status => 'NONE_FOUND', auth_type => $heading->auth_type(), tag_to_report => $authority_type->auth_tag_to_report}) if $verbose;
730             }
731         }
732
733     }
734     push(@{$results{'details'}}, { tag => '', authid => undef, status => 'UNCHANGED'}) unless %results;
735     return $num_headings_changed, \%results;
736 }
737
738 =head2 _check_valid_auth_link
739
740     if ( _check_valid_auth_link($authid, $field) ) {
741         ...
742     }
743
744 Check whether the specified heading-auth link is valid without reference
745 to Zebra. Ideally this code would be in C4::Heading, but that won't be
746 possible until we have de-cycled C4::AuthoritiesMarc, so this is the
747 safest place.
748
749 =cut
750
751 sub _check_valid_auth_link {
752     my ( $authid, $field ) = @_;
753     require C4::AuthoritiesMarc;
754
755     return C4::AuthoritiesMarc::CompareFieldWithAuthority( { 'field' => $field, 'authid' => $authid } );
756 }
757
758 =head2 GetBiblioData
759
760   $data = &GetBiblioData($biblionumber);
761
762 Returns information about the book with the given biblionumber.
763 C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
764 the C<biblio> and C<biblioitems> tables in the
765 Koha database.
766
767 In addition, C<$data-E<gt>{subject}> is the list of the book's
768 subjects, separated by C<" , "> (space, comma, space).
769 If there are multiple biblioitems with the given biblionumber, only
770 the first one is considered.
771
772 =cut
773
774 sub GetBiblioData {
775     my ($bibnum) = @_;
776     my $dbh = C4::Context->dbh;
777
778     my $query = " SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
779             FROM biblio
780             LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
781             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
782             WHERE biblio.biblionumber = ?";
783
784     my $sth = $dbh->prepare($query);
785     $sth->execute($bibnum);
786     my $data;
787     $data = $sth->fetchrow_hashref;
788     $sth->finish;
789
790     return ($data);
791 }    # sub GetBiblioData
792
793 =head2 GetISBDView 
794
795   $isbd = &GetISBDView({
796       'record'    => $marc_record,
797       'template'  => $interface, # opac/intranet
798       'framework' => $framework,
799   });
800
801 Return the ISBD view which can be included in opac and intranet
802
803 =cut
804
805 sub GetISBDView {
806     my ( $params ) = @_;
807
808     # Expecting record WITH items.
809     my $record    = $params->{record};
810     return unless defined $record;
811
812     my $template  = $params->{template} // q{};
813     my $sysprefname = $template eq 'opac' ? 'opacisbd' : 'isbd';
814     my $framework = $params->{framework};
815     my $itemtype  = $framework;
816     my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch" );
817     my $tagslib = GetMarcStructure( 1, $itemtype, { unsafe => 1 } );
818
819     my $ISBD = C4::Context->preference($sysprefname);
820     my $bloc = $ISBD;
821     my $res;
822     my $blocres;
823
824     foreach my $isbdfield ( split( /#/, $bloc ) ) {
825
826         #         $isbdfield= /(.?.?.?)/;
827         $isbdfield =~ /(\d\d\d)([^\|])?\|(.*)\|(.*)\|(.*)/;
828         my $fieldvalue = $1 || 0;
829         my $subfvalue  = $2 || "";
830         my $textbefore = $3;
831         my $analysestring = $4;
832         my $textafter     = $5;
833
834         #         warn "==> $1 / $2 / $3 / $4";
835         #         my $fieldvalue=substr($isbdfield,0,3);
836         if ( $fieldvalue > 0 ) {
837             my $hasputtextbefore = 0;
838             my @fieldslist       = $record->field($fieldvalue);
839             @fieldslist = sort { $a->subfield($holdingbrtagsubf) cmp $b->subfield($holdingbrtagsubf) } @fieldslist if ( $fieldvalue eq $holdingbrtagf );
840
841             #         warn "ERROR IN ISBD DEFINITION at : $isbdfield" unless $fieldvalue;
842             #             warn "FV : $fieldvalue";
843             if ( $subfvalue ne "" ) {
844                 # OPAC hidden subfield
845                 next
846                   if ( ( $template eq 'opac' )
847                     && ( $tagslib->{$fieldvalue}->{$subfvalue}->{'hidden'} || 0 ) > 0 );
848                 foreach my $field (@fieldslist) {
849                     foreach my $subfield ( $field->subfield($subfvalue) ) {
850                         my $calculated = $analysestring;
851                         my $tag        = $field->tag();
852                         if ( $tag < 10 ) {
853                         } else {
854                             my $subfieldvalue = GetAuthorisedValueDesc( $tag, $subfvalue, $subfield, '', $tagslib );
855                             my $tagsubf = $tag . $subfvalue;
856                             $calculated =~ s/\{(.?.?.?.?)$tagsubf(.*?)\}/$1$subfieldvalue$2\{$1$tagsubf$2\}/g;
857                             if ( $template eq "opac" ) { $calculated =~ s#/cgi-bin/koha/[^/]+/([^.]*.pl\?.*)$#opac-$1#g; }
858
859                             # field builded, store the result
860                             if ( $calculated && !$hasputtextbefore ) {    # put textbefore if not done
861                                 $blocres .= $textbefore;
862                                 $hasputtextbefore = 1;
863                             }
864
865                             # remove punctuation at start
866                             $calculated =~ s/^( |;|:|\.|-)*//g;
867                             $blocres .= $calculated;
868
869                         }
870                     }
871                 }
872                 $blocres .= $textafter if $hasputtextbefore;
873             } else {
874                 foreach my $field (@fieldslist) {
875                     my $calculated = $analysestring;
876                     my $tag        = $field->tag();
877                     if ( $tag < 10 ) {
878                     } else {
879                         my @subf = $field->subfields;
880                         for my $i ( 0 .. $#subf ) {
881                             my $valuecode     = $subf[$i][1];
882                             my $subfieldcode  = $subf[$i][0];
883                             # OPAC hidden subfield
884                             next
885                               if ( ( $template eq 'opac' )
886                                 && ( $tagslib->{$fieldvalue}->{$subfieldcode}->{'hidden'} || 0 ) > 0 );
887                             my $subfieldvalue = GetAuthorisedValueDesc( $tag, $subf[$i][0], $subf[$i][1], '', $tagslib );
888                             my $tagsubf       = $tag . $subfieldcode;
889
890                             $calculated =~ s/                  # replace all {{}} codes by the value code.
891                                   \{\{$tagsubf\}\} # catch the {{actualcode}}
892                                 /
893                                   $valuecode     # replace by the value code
894                                /gx;
895
896                             $calculated =~ s/\{(.?.?.?.?)$tagsubf(.*?)\}/$1$subfieldvalue$2\{$1$tagsubf$2\}/g;
897                             if ( $template eq "opac" ) { $calculated =~ s#/cgi-bin/koha/[^/]+/([^.]*.pl\?.*)$#opac-$1#g; }
898                         }
899
900                         # field builded, store the result
901                         if ( $calculated && !$hasputtextbefore ) {    # put textbefore if not done
902                             $blocres .= $textbefore;
903                             $hasputtextbefore = 1;
904                         }
905
906                         # remove punctuation at start
907                         $calculated =~ s/^( |;|:|\.|-)*//g;
908                         $blocres .= $calculated;
909                     }
910                 }
911                 $blocres .= $textafter if $hasputtextbefore;
912             }
913         } else {
914             $blocres .= $isbdfield;
915         }
916     }
917     $res .= $blocres;
918
919     $res =~ s/\{(.*?)\}//g;
920     $res =~ s/\\n/\n/g;
921     $res =~ s/\n/<br\/>/g;
922
923     # remove empty ()
924     $res =~ s/\(\)//g;
925
926     return $res;
927 }
928
929 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
930
931 =head2 IsMarcStructureInternal
932
933     my $tagslib = C4::Biblio::GetMarcStructure();
934     for my $tag ( sort keys %$tagslib ) {
935         next unless $tag;
936         for my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
937             next if IsMarcStructureInternal($tagslib->{$tag}{$subfield});
938         }
939         # Process subfield
940     }
941
942 GetMarcStructure creates keys (lib, tab, mandatory, repeatable, important) for a display purpose.
943 These different values should not be processed as valid subfields.
944
945 =cut
946
947 sub IsMarcStructureInternal {
948     my ( $subfield ) = @_;
949     return ref $subfield ? 0 : 1;
950 }
951
952 =head2 GetMarcStructure
953
954   $res = GetMarcStructure($forlibrarian, $frameworkcode, [ $params ]);
955
956 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
957 $forlibrarian  :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
958 $frameworkcode : the framework code to read
959 $params allows you to pass { unsafe => 1 } for better performance.
960
961 Note: If you call GetMarcStructure with unsafe => 1, do not modify or
962 even autovivify its contents. It is a cached/shared data structure. Your
963 changes c/would be passed around in subsequent calls.
964
965 =cut
966
967 sub GetMarcStructure {
968     my ( $forlibrarian, $frameworkcode, $params ) = @_;
969     $frameworkcode = "" unless $frameworkcode;
970
971     $forlibrarian = $forlibrarian ? 1 : 0;
972     my $unsafe = ($params && $params->{unsafe})? 1: 0;
973     my $cache = Koha::Caches->get_instance();
974     my $cache_key = "MarcStructure-$forlibrarian-$frameworkcode";
975     my $cached = $cache->get_from_cache($cache_key, { unsafe => $unsafe });
976     return $cached if $cached;
977
978     my $dbh = C4::Context->dbh;
979     my $sth = $dbh->prepare(
980         "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable,important,ind1_defaultvalue,ind2_defaultvalue
981         FROM marc_tag_structure 
982         WHERE frameworkcode=? 
983         ORDER BY tagfield"
984     );
985     $sth->execute($frameworkcode);
986     my ( $liblibrarian, $libopac, $tag, $res, $mandatory, $repeatable, $important, $ind1_defaultvalue, $ind2_defaultvalue );
987
988     while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable, $important, $ind1_defaultvalue, $ind2_defaultvalue ) = $sth->fetchrow ) {
989         $res->{$tag}->{lib}        = ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
990         $res->{$tag}->{tab}        = "";
991         $res->{$tag}->{mandatory}  = $mandatory;
992         $res->{$tag}->{important}  = $important;
993         $res->{$tag}->{repeatable} = $repeatable;
994     $res->{$tag}->{ind1_defaultvalue} = $ind1_defaultvalue;
995     $res->{$tag}->{ind2_defaultvalue} = $ind2_defaultvalue;
996     }
997
998     my $mss = Koha::MarcSubfieldStructures->search( { frameworkcode => $frameworkcode } )->unblessed;
999     for my $m (@$mss) {
1000         $res->{ $m->{tagfield} }->{ $m->{tagsubfield} } = {
1001             lib => ( $forlibrarian or !$m->{libopac} ) ? $m->{liblibrarian} : $m->{libopac},
1002             subfield => $m->{tagsubfield},
1003             %$m
1004         };
1005     }
1006
1007     $cache->set_in_cache($cache_key, $res);
1008     return $res;
1009 }
1010
1011 =head2 GetUsedMarcStructure
1012
1013 The same function as GetMarcStructure except it just takes field
1014 in tab 0-9. (used field)
1015
1016   my $results = GetUsedMarcStructure($frameworkcode);
1017
1018 C<$results> is a ref to an array which each case contains a ref
1019 to a hash which each keys is the columns from marc_subfield_structure
1020
1021 C<$frameworkcode> is the framework code. 
1022
1023 =cut
1024
1025 sub GetUsedMarcStructure {
1026     my $frameworkcode = shift || '';
1027     my $query = q{
1028         SELECT *
1029         FROM   marc_subfield_structure
1030         WHERE   tab > -1 
1031             AND frameworkcode = ?
1032         ORDER BY tagfield, display_order, tagsubfield
1033     };
1034     my $sth = C4::Context->dbh->prepare($query);
1035     $sth->execute($frameworkcode);
1036     return $sth->fetchall_arrayref( {} );
1037 }
1038
1039 =pod
1040
1041 =head2 GetMarcSubfieldStructure
1042
1043   my $structure = GetMarcSubfieldStructure($frameworkcode, [$params]);
1044
1045 Returns a reference to hash representing MARC subfield structure
1046 for framework with framework code C<$frameworkcode>, C<$params> is
1047 optional and may contain additional options.
1048
1049 =over 4
1050
1051 =item C<$frameworkcode>
1052
1053 The framework code.
1054
1055 =item C<$params>
1056
1057 An optional hash reference with additional options.
1058 The following options are supported:
1059
1060 =over 4
1061
1062 =item unsafe
1063
1064 Pass { unsafe => 1 } do disable cached object cloning,
1065 and instead get a shared reference, resulting in better
1066 performance (but care must be taken so that retured object
1067 is never modified).
1068
1069 Note: If you call GetMarcSubfieldStructure with unsafe => 1, do not modify or
1070 even autovivify its contents. It is a cached/shared data structure. Your
1071 changes would be passed around in subsequent calls.
1072
1073 =back
1074
1075 =back
1076
1077 =cut
1078
1079 sub GetMarcSubfieldStructure {
1080     my ( $frameworkcode, $params ) = @_;
1081
1082     $frameworkcode //= '';
1083
1084     my $cache     = Koha::Caches->get_instance();
1085     my $cache_key = "MarcSubfieldStructure-$frameworkcode";
1086     my $cached  = $cache->get_from_cache($cache_key, { unsafe => ($params && $params->{unsafe}) });
1087     return $cached if $cached;
1088
1089     my $dbh = C4::Context->dbh;
1090     # We moved to selectall_arrayref since selectall_hashref does not
1091     # keep duplicate mappings on kohafield (like place in 260 vs 264)
1092     my $subfield_aref = $dbh->selectall_arrayref( q|
1093         SELECT *
1094         FROM marc_subfield_structure
1095         WHERE frameworkcode = ?
1096         AND kohafield > ''
1097         ORDER BY frameworkcode, tagfield, display_order, tagsubfield
1098     |, { Slice => {} }, $frameworkcode );
1099     # Now map the output to a hash structure
1100     my $subfield_structure = {};
1101     foreach my $row ( @$subfield_aref ) {
1102         push @{ $subfield_structure->{ $row->{kohafield} }}, $row;
1103     }
1104     $cache->set_in_cache( $cache_key, $subfield_structure );
1105     return $subfield_structure;
1106 }
1107
1108 =head2 GetMarcFromKohaField
1109
1110     ( $field,$subfield ) = GetMarcFromKohaField( $kohafield );
1111     @fields = GetMarcFromKohaField( $kohafield );
1112     $field = GetMarcFromKohaField( $kohafield );
1113
1114     Returns the MARC fields & subfields mapped to $kohafield.
1115     Since the Default framework is considered as authoritative for such
1116     mappings, the former frameworkcode parameter is obsoleted.
1117
1118     In list context all mappings are returned; there can be multiple
1119     mappings. Note that in the above example you could miss a second
1120     mappings in the first call.
1121     In scalar context only the field tag of the first mapping is returned.
1122
1123 =cut
1124
1125 sub GetMarcFromKohaField {
1126     my ( $kohafield ) = @_;
1127     return unless $kohafield;
1128     # The next call uses the Default framework since it is AUTHORITATIVE
1129     # for all Koha to MARC mappings.
1130     my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
1131     my @retval;
1132     foreach( @{ $mss->{$kohafield} } ) {
1133         push @retval, $_->{tagfield}, $_->{tagsubfield};
1134     }
1135     return wantarray ? @retval : ( @retval ? $retval[0] : undef );
1136 }
1137
1138 =head2 GetMarcSubfieldStructureFromKohaField
1139
1140     my $str = GetMarcSubfieldStructureFromKohaField( $kohafield );
1141
1142     Returns marc subfield structure information for $kohafield.
1143     The Default framework is used, since it is authoritative for kohafield
1144     mappings.
1145     In list context returns a list of all hashrefs, since there may be
1146     multiple mappings. In scalar context the first hashref is returned.
1147
1148 =cut
1149
1150 sub GetMarcSubfieldStructureFromKohaField {
1151     my ( $kohafield ) = @_;
1152
1153     return unless $kohafield;
1154
1155     # The next call uses the Default framework since it is AUTHORITATIVE
1156     # for all Koha to MARC mappings.
1157     my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
1158     return unless $mss->{$kohafield};
1159     return wantarray ? @{$mss->{$kohafield}} : $mss->{$kohafield}->[0];
1160 }
1161
1162 =head2 GetMarcBiblio
1163
1164   my $record = GetMarcBiblio({
1165       biblionumber => $biblionumber,
1166       embed_items  => $embeditems,
1167       opac         => $opac,
1168       borcat       => $patron_category });
1169
1170 Returns MARC::Record representing a biblio record, or C<undef> if the
1171 biblionumber doesn't exist.
1172
1173 Both embed_items and opac are optional.
1174 If embed_items is passed and is 1, items are embedded.
1175 If opac is passed and is 1, the record is filtered as needed.
1176
1177 =over 4
1178
1179 =item C<$biblionumber>
1180
1181 the biblionumber
1182
1183 =item C<$embeditems>
1184
1185 set to true to include item information.
1186
1187 =item C<$opac>
1188
1189 set to true to make the result suited for OPAC view. This causes things like
1190 OpacHiddenItems to be applied.
1191
1192 =item C<$borcat>
1193
1194 If the OpacHiddenItemsExceptions system preference is set, this patron category
1195 can be used to make visible OPAC items which would be normally hidden.
1196 It only makes sense in combination both embed_items and opac values true.
1197
1198 =back
1199
1200 =cut
1201
1202 sub GetMarcBiblio {
1203     my ($params) = @_;
1204
1205     if (not defined $params) {
1206         carp 'GetMarcBiblio called without parameters';
1207         return;
1208     }
1209
1210     my $biblionumber = $params->{biblionumber};
1211     my $embeditems   = $params->{embed_items} || 0;
1212     my $opac         = $params->{opac} || 0;
1213     my $borcat       = $params->{borcat} // q{};
1214
1215     if (not defined $biblionumber) {
1216         carp 'GetMarcBiblio called with undefined biblionumber';
1217         return;
1218     }
1219
1220     my $marcxml = GetXmlBiblio( $biblionumber );
1221     $marcxml = StripNonXmlChars( $marcxml );
1222     MARC::File::XML->default_record_format( C4::Context->preference('marcflavour') );
1223     my $record = MARC::Record->new();
1224
1225     if ($marcxml) {
1226         $record = eval {
1227             MARC::Record::new_from_xml( $marcxml, "UTF-8",
1228                 C4::Context->preference('marcflavour') );
1229         };
1230         if ($@) { warn " problem with :$biblionumber : $@ \n$marcxml"; }
1231         return unless $record;
1232
1233         C4::Biblio::EmbedItemsInMarcBiblio({
1234             marc_record  => $record,
1235             biblionumber => $biblionumber,
1236             opac         => $opac,
1237             borcat       => $borcat })
1238           if ($embeditems);
1239
1240         return $record;
1241     }
1242     else {
1243         return;
1244     }
1245 }
1246
1247 =head2 GetXmlBiblio
1248
1249   my $marcxml = GetXmlBiblio($biblionumber);
1250
1251 Returns biblio_metadata.metadata/marcxml of the biblionumber passed in parameter.
1252 The XML should only contain biblio information (item information is no longer stored in marcxml field)
1253
1254 =cut
1255
1256 sub GetXmlBiblio {
1257     my ($biblionumber) = @_;
1258     my $dbh = C4::Context->dbh;
1259     return unless $biblionumber;
1260     my ($marcxml) = $dbh->selectrow_array(
1261         q|
1262         SELECT metadata
1263         FROM biblio_metadata
1264         WHERE biblionumber=?
1265             AND format='marcxml'
1266             AND `schema`=?
1267     |, undef, $biblionumber, C4::Context->preference('marcflavour')
1268     );
1269     return $marcxml;
1270 }
1271
1272 =head2 GetMarcPrice
1273
1274 return the prices in accordance with the Marc format.
1275
1276 returns 0 if no price found
1277 returns undef if called without a marc record or with
1278 an unrecognized marc format
1279
1280 =cut
1281
1282 sub GetMarcPrice {
1283     my ( $record, $marcflavour ) = @_;
1284     if (!$record) {
1285         carp 'GetMarcPrice called on undefined record';
1286         return;
1287     }
1288
1289     my @listtags;
1290     my $subfield;
1291     
1292     if ( $marcflavour eq "MARC21" ) {
1293         @listtags = ('345', '020');
1294         $subfield="c";
1295     } elsif ( $marcflavour eq "UNIMARC" ) {
1296         @listtags = ('345', '010');
1297         $subfield="d";
1298     } else {
1299         return;
1300     }
1301     
1302     for my $field ( $record->field(@listtags) ) {
1303         for my $subfield_value  ($field->subfield($subfield)){
1304             #check value
1305             $subfield_value = MungeMarcPrice( $subfield_value );
1306             return $subfield_value if ($subfield_value);
1307         }
1308     }
1309     return 0; # no price found
1310 }
1311
1312 =head2 MungeMarcPrice
1313
1314 Return the best guess at what the actual price is from a price field.
1315
1316 =cut
1317
1318 sub MungeMarcPrice {
1319     my ( $price ) = @_;
1320     return unless ( $price =~ m/\d/ ); ## No digits means no price.
1321     # Look for the currency symbol and the normalized code of the active currency, if it's there,
1322     my $active_currency = Koha::Acquisition::Currencies->get_active;
1323     my $symbol = $active_currency->symbol;
1324     my $isocode = $active_currency->isocode;
1325     $isocode = $active_currency->currency unless defined $isocode;
1326     my $localprice;
1327     if ( $symbol ) {
1328         my @matches =($price=~ /
1329             \s?
1330             (                          # start of capturing parenthesis
1331             (?:
1332             (?:[\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'
1333             |(?:\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'
1334             )
1335             \s?\p{Sc}?\s?              # followed or not by a whitespace. \p{Sc}?\s? are for cases like '25$ USD'
1336             (?:
1337             (?:[\p{Sc}\p{L}\/.]){1,4}  # followed by same block as symbol block
1338             |(?:\d+[\p{P}\s]?){1,4}    # or by same block as digits block
1339             )
1340             \s?\p{L}{0,4}\s?           # followed or not by a whitespace. \p{L}{0,4}\s? are for cases like '$9.50 USD'
1341             )                          # end of capturing parenthesis
1342             (?:\p{P}|\z)               # followed by a punctuation sign or by the end of the string
1343             /gx);
1344
1345         if ( @matches ) {
1346             foreach ( @matches ) {
1347                 $localprice = $_ and last if index($_, $isocode)>=0;
1348             }
1349             if ( !$localprice ) {
1350                 foreach ( @matches ) {
1351                     $localprice = $_ and last if $_=~ /(^|[^\p{Sc}\p{L}\/])\Q$symbol\E([^\p{Sc}\p{L}\/]+\z|\z)/;
1352                 }
1353             }
1354         }
1355     }
1356     if ( $localprice ) {
1357         $price = $localprice;
1358     } else {
1359         ## Grab the first number in the string ( can use commas or periods for thousands separator and/or decimal separator )
1360         ( $price ) = $price =~ m/([\d\,\.]+[[\,\.]\d\d]?)/;
1361     }
1362     # eliminate symbol/isocode, space and any final dot from the string
1363     $price =~ s/[\p{Sc}\p{L}\/ ]|\.$//g;
1364     # remove comma,dot when used as separators from hundreds
1365     $price =~s/[\,\.](\d{3})/$1/g;
1366     # convert comma to dot to ensure correct display of decimals if existing
1367     $price =~s/,/./;
1368     return $price;
1369 }
1370
1371
1372 =head2 GetMarcQuantity
1373
1374 return the quantity of a book. Used in acquisition only, when importing a file an iso2709 from a bookseller
1375 Warning : this is not really in the marc standard. In Unimarc, Electre (the most widely used bookseller) use the 969$a
1376
1377 returns 0 if no quantity found
1378 returns undef if called without a marc record or with
1379 an unrecognized marc format
1380
1381 =cut
1382
1383 sub GetMarcQuantity {
1384     my ( $record, $marcflavour ) = @_;
1385     if (!$record) {
1386         carp 'GetMarcQuantity called on undefined record';
1387         return;
1388     }
1389
1390     my @listtags;
1391     my $subfield;
1392     
1393     if ( $marcflavour eq "MARC21" ) {
1394         return 0
1395     } elsif ( $marcflavour eq "UNIMARC" ) {
1396         @listtags = ('969');
1397         $subfield="a";
1398     } else {
1399         return;
1400     }
1401     
1402     for my $field ( $record->field(@listtags) ) {
1403         for my $subfield_value  ($field->subfield($subfield)){
1404             #check value
1405             if ($subfield_value) {
1406                  # in France, the cents separator is the , but sometimes, ppl use a .
1407                  # in this case, the price will be x100 when unformatted ! Replace the . by a , to get a proper price calculation
1408                 $subfield_value =~ s/\./,/ if C4::Context->preference("CurrencyFormat") eq "FR";
1409                 return $subfield_value;
1410             }
1411         }
1412     }
1413     return 0; # no price found
1414 }
1415
1416
1417 =head2 GetAuthorisedValueDesc
1418
1419   my $subfieldvalue =get_authorised_value_desc(
1420     $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category, $opac);
1421
1422 Retrieve the complete description for a given authorised value.
1423
1424 Now takes $category and $value pair too.
1425
1426   my $auth_value_desc =GetAuthorisedValueDesc(
1427     '','', 'DVD' ,'','','CCODE');
1428
1429 If the optional $opac parameter is set to a true value, displays OPAC 
1430 descriptions rather than normal ones when they exist.
1431
1432 =cut
1433
1434 sub GetAuthorisedValueDesc {
1435     my ( $tag, $subfield, $value, $framework, $tagslib, $category, $opac ) = @_;
1436
1437     if ( !$category ) {
1438
1439         return $value unless defined $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1440
1441         #---- branch
1442         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1443             my $branch = Koha::Libraries->find($value);
1444             return $branch? $branch->branchname: q{};
1445         }
1446
1447         #---- itemtypes
1448         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1449             my $itemtype = Koha::ItemTypes->find( $value );
1450             return $itemtype ? $itemtype->translated_description : q||;
1451         }
1452
1453         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "cn_source" ) {
1454             my $source = GetClassSource($value);
1455             return $source ? $source->{description} : q||;
1456         }
1457
1458         #---- "true" authorized value
1459         $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1460     }
1461
1462     my $dbh = C4::Context->dbh;
1463     if ( $category ne "" ) {
1464         my $sth = $dbh->prepare( "SELECT lib, lib_opac FROM authorised_values WHERE category = ? AND authorised_value = ?" );
1465         $sth->execute( $category, $value );
1466         my $data = $sth->fetchrow_hashref;
1467         return ( $opac && $data->{'lib_opac'} ) ? $data->{'lib_opac'} : $data->{'lib'};
1468     } else {
1469         return $value;    # if nothing is found return the original value
1470     }
1471 }
1472
1473 =head2 GetMarcControlnumber
1474
1475   $marccontrolnumber = GetMarcControlnumber($record,$marcflavour);
1476
1477 Get the control number / record Identifier from the MARC record and return it.
1478
1479 =cut
1480
1481 sub GetMarcControlnumber {
1482     my ( $record, $marcflavour ) = @_;
1483     if (!$record) {
1484         carp 'GetMarcControlnumber called on undefined record';
1485         return;
1486     }
1487     my $controlnumber = "";
1488     # Control number or Record identifier are the same field in MARC21 and UNIMARC
1489     # Keep $marcflavour for possible later use
1490     if ($marcflavour eq "MARC21" || $marcflavour eq "UNIMARC" ) {
1491         my $controlnumberField = $record->field('001');
1492         if ($controlnumberField) {
1493             $controlnumber = $controlnumberField->data();
1494         }
1495     }
1496     return $controlnumber;
1497 }
1498
1499 =head2 GetMarcISBN
1500
1501   $marcisbnsarray = GetMarcISBN( $record, $marcflavour );
1502
1503 Get all ISBNs from the MARC record and returns them in an array.
1504 ISBNs stored in different fields depending on MARC flavour
1505
1506 =cut
1507
1508 sub GetMarcISBN {
1509     my ( $record, $marcflavour ) = @_;
1510     if (!$record) {
1511         carp 'GetMarcISBN called on undefined record';
1512         return;
1513     }
1514     my $scope;
1515     if ( $marcflavour eq "UNIMARC" ) {
1516         $scope = '010';
1517     } else {    # assume marc21 if not unimarc
1518         $scope = '020';
1519     }
1520
1521     my @marcisbns;
1522     foreach my $field ( $record->field($scope) ) {
1523         my $isbn = $field->subfield( 'a' );
1524         if ( $isbn && $isbn ne "" ) {
1525             push @marcisbns, $isbn;
1526         }
1527     }
1528
1529     return \@marcisbns;
1530 }    # end GetMarcISBN
1531
1532
1533 =head2 GetMarcISSN
1534
1535   $marcissnsarray = GetMarcISSN( $record, $marcflavour );
1536
1537 Get all valid ISSNs from the MARC record and returns them in an array.
1538 ISSNs are stored in different fields depending on MARC flavour
1539
1540 =cut
1541
1542 sub GetMarcISSN {
1543     my ( $record, $marcflavour ) = @_;
1544     if (!$record) {
1545         carp 'GetMarcISSN called on undefined record';
1546         return;
1547     }
1548     my $scope;
1549     if ( $marcflavour eq "UNIMARC" ) {
1550         $scope = '011';
1551     }
1552     else {    # assume MARC21
1553         $scope = '022';
1554     }
1555     my @marcissns;
1556     foreach my $field ( $record->field($scope) ) {
1557         push @marcissns, $field->subfield( 'a' )
1558             if ( $field->subfield( 'a' ) ne "" );
1559     }
1560     return \@marcissns;
1561 }    # end GetMarcISSN
1562
1563 =head2 GetMarcSubjects
1564
1565   $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1566
1567 Get all subjects from the MARC record and returns them in an array.
1568 The subjects are stored in different fields depending on MARC flavour
1569
1570 =cut
1571
1572 sub GetMarcSubjects {
1573     my ( $record, $marcflavour ) = @_;
1574     if (!$record) {
1575         carp 'GetMarcSubjects called on undefined record';
1576         return;
1577     }
1578     my ( $mintag, $maxtag, $fields_filter );
1579     if ( $marcflavour eq "UNIMARC" ) {
1580         $mintag = "600";
1581         $maxtag = "611";
1582         $fields_filter = '6..';
1583     } else { # marc21
1584         $mintag = "600";
1585         $maxtag = "699";
1586         $fields_filter = '6..';
1587     }
1588
1589     my @marcsubjects;
1590
1591     my $subject_limit = C4::Context->preference("TraceCompleteSubfields") ? 'su,complete-subfield' : 'su';
1592     my $AuthoritySeparator = C4::Context->preference('AuthoritySeparator');
1593
1594     foreach my $field ( $record->field($fields_filter) ) {
1595         next unless ($field->tag() >= $mintag && $field->tag() <= $maxtag);
1596         my @subfields_loop;
1597         my @subfields = $field->subfields();
1598         my @link_loop;
1599
1600         # if there is an authority link, build the links with an= subfield9
1601         my $subfield9 = $field->subfield('9');
1602         my $authoritylink;
1603         if ($subfield9) {
1604             my $linkvalue = $subfield9;
1605             $linkvalue =~ s/(\(|\))//g;
1606             @link_loop = ( { limit => 'an', 'link' => $linkvalue } );
1607             $authoritylink = $linkvalue
1608         }
1609
1610         # other subfields
1611         for my $subject_subfield (@subfields) {
1612             next if ( $subject_subfield->[0] eq '9' );
1613
1614             # don't load unimarc subfields 3,4,5
1615             next if ( ( $marcflavour eq "UNIMARC" ) and ( $subject_subfield->[0] =~ /2|3|4|5/ ) );
1616             # don't load MARC21 subfields 2 (FIXME: any more subfields??)
1617             next if ( ( $marcflavour eq "MARC21" ) and ( $subject_subfield->[0] =~ /2/ ) );
1618
1619             my $code      = $subject_subfield->[0];
1620             my $value     = $subject_subfield->[1];
1621             my $linkvalue = $value;
1622             $linkvalue =~ s/(\(|\))//g;
1623             # if no authority link, build a search query
1624             unless ($subfield9) {
1625                 push @link_loop, {
1626                     limit    => $subject_limit,
1627                     'link'   => $linkvalue,
1628                     operator => (scalar @link_loop) ? ' AND ' : undef
1629                 };
1630             }
1631             my @this_link_loop = @link_loop;
1632             # do not display $0
1633             unless ( $code eq '0' ) {
1634                 push @subfields_loop, {
1635                     code      => $code,
1636                     value     => $value,
1637                     link_loop => \@this_link_loop,
1638                     separator => (scalar @subfields_loop) ? $AuthoritySeparator : ''
1639                 };
1640             }
1641         }
1642
1643         push @marcsubjects, {
1644             MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop,
1645             authoritylink => $authoritylink,
1646         } if $authoritylink || @subfields_loop;
1647
1648     }
1649     return \@marcsubjects;
1650 }    #end getMARCsubjects
1651
1652 =head2 GetMarcUrls
1653
1654   $marcurls = GetMarcUrls($record,$marcflavour);
1655
1656 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1657 Assumes web resources (not uncommon in MARC21 to omit resource type ind) 
1658
1659 =cut
1660
1661 sub GetMarcUrls {
1662     my ( $record, $marcflavour ) = @_;
1663     if (!$record) {
1664         carp 'GetMarcUrls called on undefined record';
1665         return;
1666     }
1667
1668     my @marcurls;
1669     for my $field ( $record->field('856') ) {
1670         my @notes;
1671         for my $note ( $field->subfield('z') ) {
1672             push @notes, { note => $note };
1673         }
1674         my @urls = $field->subfield('u');
1675         foreach my $url (@urls) {
1676             $url =~ s/^\s+|\s+$//g; # trim
1677             my $marcurl;
1678             if ( $marcflavour eq 'MARC21' ) {
1679                 my $s3   = $field->subfield('3');
1680                 my $link = $field->subfield('y');
1681                 unless ( $url =~ /^\w+:/ ) {
1682                     if ( $field->indicator(1) eq '7' ) {
1683                         $url = $field->subfield('2') . "://" . $url;
1684                     } elsif ( $field->indicator(1) eq '1' ) {
1685                         $url = 'ftp://' . $url;
1686                     } else {
1687
1688                         #  properly, this should be if ind1=4,
1689                         #  however we will assume http protocol since we're building a link.
1690                         $url = 'http://' . $url;
1691                     }
1692                 }
1693
1694                 # TODO handle ind 2 (relationship)
1695                 $marcurl = {
1696                     MARCURL => $url,
1697                     notes   => \@notes,
1698                 };
1699                 $marcurl->{'linktext'} = $link || $s3 || C4::Context->preference('URLLinkText') || $url;
1700                 $marcurl->{'part'} = $s3 if ($link);
1701                 $marcurl->{'toc'} = 1 if ( defined($s3) && $s3 =~ /^[Tt]able/ );
1702             } else {
1703                 $marcurl->{'linktext'} = $field->subfield('2') || C4::Context->preference('URLLinkText') || $url;
1704                 $marcurl->{'MARCURL'} = $url;
1705             }
1706             push @marcurls, $marcurl;
1707         }
1708     }
1709     return \@marcurls;
1710 }
1711
1712 =head2 GetMarcSeries
1713
1714   $marcseriesarray = GetMarcSeries($record,$marcflavour);
1715
1716 Get all series from the MARC record and returns them in an array.
1717 The series are stored in different fields depending on MARC flavour
1718
1719 =cut
1720
1721 sub GetMarcSeries {
1722     my ( $record, $marcflavour ) = @_;
1723     if (!$record) {
1724         carp 'GetMarcSeries called on undefined record';
1725         return;
1726     }
1727
1728     my ( $mintag, $maxtag, $fields_filter );
1729     if ( $marcflavour eq "UNIMARC" ) {
1730         $mintag = "225";
1731         $maxtag = "225";
1732         $fields_filter = '2..';
1733     } else {    # marc21
1734         $mintag = "440";
1735         $maxtag = "490";
1736         $fields_filter = '4..';
1737     }
1738
1739     my @marcseries;
1740     my $AuthoritySeparator = C4::Context->preference('AuthoritySeparator');
1741
1742     foreach my $field ( $record->field($fields_filter) ) {
1743         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1744         my @subfields_loop;
1745         my @subfields = $field->subfields();
1746         my @link_loop;
1747
1748         for my $series_subfield (@subfields) {
1749
1750             # ignore $9, used for authority link
1751             next if ( $series_subfield->[0] eq '9' );
1752
1753             my $volume_number;
1754             my $code      = $series_subfield->[0];
1755             my $value     = $series_subfield->[1];
1756             my $linkvalue = $value;
1757             $linkvalue =~ s/(\(|\))//g;
1758
1759             # see if this is an instance of a volume
1760             if ( $code eq 'v' ) {
1761                 $volume_number = 1;
1762             }
1763
1764             push @link_loop, {
1765                 'link' => $linkvalue,
1766                 operator => (scalar @link_loop) ? ' AND ' : undef
1767             };
1768
1769             if ($volume_number) {
1770                 push @subfields_loop, { volumenum => $value };
1771             } else {
1772                 push @subfields_loop, {
1773                     code      => $code,
1774                     value     => $value,
1775                     link_loop => \@link_loop,
1776                     separator => (scalar @subfields_loop) ? $AuthoritySeparator : '',
1777                     volumenum => $volume_number,
1778                 }
1779             }
1780         }
1781         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1782
1783     }
1784     return \@marcseries;
1785 }    #end getMARCseriess
1786
1787 =head2 UpsertMarcSubfield
1788
1789     my $record = C4::Biblio::UpsertMarcSubfield($MARC::Record, $fieldTag, $subfieldCode, $subfieldContent);
1790
1791 =cut
1792
1793 sub UpsertMarcSubfield {
1794     my ($record, $tag, $code, $content) = @_;
1795     my $f = $record->field($tag);
1796
1797     if ($f) {
1798         $f->update( $code => $content );
1799     }
1800     else {
1801         my $f = MARC::Field->new( $tag, '', '', $code => $content);
1802         $record->insert_fields_ordered( $f );
1803     }
1804 }
1805
1806 =head2 UpsertMarcControlField
1807
1808     my $record = C4::Biblio::UpsertMarcControlField($MARC::Record, $fieldTag, $content);
1809
1810 =cut
1811
1812 sub UpsertMarcControlField {
1813     my ($record, $tag, $content) = @_;
1814     die "UpsertMarcControlField() \$tag '$tag' is not a control field\n" unless 0+$tag < 10;
1815     my $f = $record->field($tag);
1816
1817     if ($f) {
1818         $f->update( $content );
1819     }
1820     else {
1821         my $f = MARC::Field->new($tag, $content);
1822         $record->insert_fields_ordered( $f );
1823     }
1824 }
1825
1826 =head2 GetFrameworkCode
1827
1828   $frameworkcode = GetFrameworkCode( $biblionumber )
1829
1830 =cut
1831
1832 sub GetFrameworkCode {
1833     my ($biblionumber) = @_;
1834     my $dbh            = C4::Context->dbh;
1835     my $sth            = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
1836     $sth->execute($biblionumber);
1837     my ($frameworkcode) = $sth->fetchrow;
1838     return $frameworkcode;
1839 }
1840
1841 =head2 TransformKohaToMarc
1842
1843     $record = TransformKohaToMarc( $hash [, $params ]  )
1844
1845 This function builds a (partial) MARC::Record from a hash.
1846 Hash entries can be from biblio, biblioitems or items.
1847 The params hash includes the parameter no_split used in C4::Items.
1848
1849 This function is called in acquisition module, to create a basic catalogue
1850 entry from user entry.
1851
1852 =cut
1853
1854
1855 sub TransformKohaToMarc {
1856     my ( $hash, $params ) = @_;
1857     my $record = MARC::Record->new();
1858     SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
1859
1860     # In the next call we use the Default framework, since it is considered
1861     # authoritative for Koha to Marc mappings.
1862     my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # do not change framework
1863     my $tag_hr = {};
1864     while ( my ($kohafield, $value) = each %$hash ) {
1865         foreach my $fld ( @{ $mss->{$kohafield} } ) {
1866             my $tagfield    = $fld->{tagfield};
1867             my $tagsubfield = $fld->{tagsubfield};
1868             next if !$tagfield;
1869
1870             # BZ 21800: split value if field is repeatable.
1871             my @values = _check_split($params, $fld, $value)
1872                 ? split(/\s?\|\s?/, $value, -1)
1873                 : ( $value );
1874             foreach my $value ( @values ) {
1875                 next if $value eq '';
1876                 $tag_hr->{$tagfield} //= [];
1877                 push @{$tag_hr->{$tagfield}}, [($tagsubfield, $value)];
1878             }
1879         }
1880     }
1881     foreach my $tag (sort keys %$tag_hr) {
1882         my @sfl = @{$tag_hr->{$tag}};
1883         @sfl = sort { $a->[0] cmp $b->[0]; } @sfl;
1884         @sfl = map { @{$_}; } @sfl;
1885         # Special care for control fields: remove the subfield indication @
1886         # and do not insert indicators.
1887         my @ind = $tag < 10 ? () : ( " ", " " );
1888         @sfl = grep { $_ ne '@' } @sfl if $tag < 10;
1889         $record->insert_fields_ordered( MARC::Field->new($tag, @ind, @sfl) );
1890     }
1891     return $record;
1892 }
1893
1894 sub _check_split {
1895 # Checks if $value must be split; may consult passed framework
1896     my ($params, $fld, $value) = @_;
1897     return if index($value,'|') == -1; # nothing to worry about
1898     return if $params->{no_split};
1899
1900     # if we did not get a specific framework, check default in $mss
1901     return $fld->{repeatable} if !$params->{framework};
1902
1903     # here we need to check the specific framework
1904     my $mss = GetMarcSubfieldStructure($params->{framework}, { unsafe => 1 });
1905     foreach my $fld2 ( @{ $mss->{ $fld->{kohafield} } } ) {
1906         next if $fld2->{tagfield} ne $fld->{tagfield};
1907         next if $fld2->{tagsubfield} ne $fld->{tagsubfield};
1908         return 1 if $fld2->{repeatable};
1909     }
1910     return;
1911 }
1912
1913 =head2 PrepHostMarcField
1914
1915     $hostfield = PrepHostMarcField ( $hostbiblionumber,$hostitemnumber,$marcflavour )
1916
1917 This function returns a host field populated with data from the host record, the field can then be added to an analytical record
1918
1919 =cut
1920
1921 sub PrepHostMarcField {
1922     my ($hostbiblionumber,$hostitemnumber, $marcflavour) = @_;
1923     $marcflavour ||="MARC21";
1924     
1925     my $hostrecord = GetMarcBiblio({ biblionumber => $hostbiblionumber });
1926     my $item = Koha::Items->find($hostitemnumber);
1927
1928         my $hostmarcfield;
1929     if ( $marcflavour eq "MARC21" ) {
1930         
1931         #main entry
1932         my $mainentry;
1933         if ($hostrecord->subfield('100','a')){
1934             $mainentry = $hostrecord->subfield('100','a');
1935         } elsif ($hostrecord->subfield('110','a')){
1936             $mainentry = $hostrecord->subfield('110','a');
1937         } else {
1938             $mainentry = $hostrecord->subfield('111','a');
1939         }
1940         
1941         # qualification info
1942         my $qualinfo;
1943         if (my $field260 = $hostrecord->field('260')){
1944             $qualinfo =  $field260->as_string( 'abc' );
1945         }
1946         
1947
1948         #other fields
1949         my $ed = $hostrecord->subfield('250','a');
1950         my $barcode = $item->barcode;
1951         my $title = $hostrecord->subfield('245','a');
1952
1953         # record control number, 001 with 003 and prefix
1954         my $recctrlno;
1955         if ($hostrecord->field('001')){
1956             $recctrlno = $hostrecord->field('001')->data();
1957             if ($hostrecord->field('003')){
1958                 $recctrlno = '('.$hostrecord->field('003')->data().')'.$recctrlno;
1959             }
1960         }
1961
1962         # issn/isbn
1963         my $issn = $hostrecord->subfield('022','a');
1964         my $isbn = $hostrecord->subfield('020','a');
1965
1966
1967         $hostmarcfield = MARC::Field->new(
1968                 773, '0', '',
1969                 '0' => $hostbiblionumber,
1970                 '9' => $hostitemnumber,
1971                 'a' => $mainentry,
1972                 'b' => $ed,
1973                 'd' => $qualinfo,
1974                 'o' => $barcode,
1975                 't' => $title,
1976                 'w' => $recctrlno,
1977                 'x' => $issn,
1978                 'z' => $isbn
1979                 );
1980     } elsif ($marcflavour eq "UNIMARC") {
1981         $hostmarcfield = MARC::Field->new(
1982             461, '', '',
1983             '0' => $hostbiblionumber,
1984             't' => $hostrecord->subfield('200','a'), 
1985             '9' => $hostitemnumber
1986         );      
1987     };
1988
1989     return $hostmarcfield;
1990 }
1991
1992 =head2 TransformHtmlToXml
1993
1994   $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, 
1995                              $ind_tag, $auth_type )
1996
1997 $auth_type contains :
1998
1999 =over
2000
2001 =item - nothing : rebuild a biblio. In UNIMARC the encoding is in 100$a pos 26/27
2002
2003 =item - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
2004
2005 =item - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
2006
2007 =back
2008
2009 =cut
2010
2011 sub TransformHtmlToXml {
2012     my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
2013     # NOTE: The parameter $ind_tag is NOT USED -- BZ 11247
2014
2015     my ( $perm_loc_tag, $perm_loc_subfield ) = C4::Biblio::GetMarcFromKohaField( "items.permanent_location" );
2016
2017     my $xml = MARC::File::XML::header('UTF-8');
2018     $xml .= "<record>\n";
2019     $auth_type = C4::Context->preference('marcflavour') unless $auth_type; # FIXME auth_type must be removed
2020     MARC::File::XML->default_record_format($auth_type);
2021
2022     # in UNIMARC, field 100 contains the encoding
2023     # check that there is one, otherwise the
2024     # MARC::Record->new_from_xml will fail (and Koha will die)
2025     my $unimarc_and_100_exist = 0;
2026     $unimarc_and_100_exist = 1 if $auth_type eq 'ITEM';    # if we rebuild an item, no need of a 100 field
2027     my $prevtag = -1;
2028     my $first   = 1;
2029     my $j       = -1;
2030     my $close_last_tag;
2031     for ( my $i = 0 ; $i < @$tags ; $i++ ) {
2032         if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a" ) {
2033
2034             # if we have a 100 field and it's values are not correct, skip them.
2035             # if we don't have any valid 100 field, we will create a default one at the end
2036             my $enc = substr( @$values[$i], 26, 2 );
2037             if ( $enc eq '01' or $enc eq '50' or $enc eq '03' ) {
2038                 $unimarc_and_100_exist = 1;
2039             } else {
2040                 next;
2041             }
2042         }
2043         @$values[$i] =~ s/&/&amp;/g;
2044         @$values[$i] =~ s/</&lt;/g;
2045         @$values[$i] =~ s/>/&gt;/g;
2046         @$values[$i] =~ s/"/&quot;/g;
2047         @$values[$i] =~ s/'/&apos;/g;
2048
2049         my $skip = @$values[$i] eq q{};
2050         $skip = 0
2051           if $perm_loc_tag
2052           && $perm_loc_subfield
2053           && @$tags[$i] eq $perm_loc_tag
2054           && @$subfields[$i] eq $perm_loc_subfield;
2055
2056         if ( ( @$tags[$i] ne $prevtag ) ) {
2057             $close_last_tag = 0;
2058             $j++ unless ( @$tags[$i] eq "" );
2059             my $str = ( $indicator->[$j] // q{} ) . '  '; # extra space prevents substr outside of string warn
2060             my $ind1 = _default_ind_to_space( substr( $str, 0, 1 ) );
2061             my $ind2 = _default_ind_to_space( substr( $str, 1, 1 ) );
2062             if ( !$first ) {
2063                 $xml .= "</datafield>\n";
2064                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
2065                     && ( !$skip ) ) {
2066                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2067                     $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2068                     $first = 0;
2069                     $close_last_tag = 1;
2070                 } else {
2071                     $first = 1;
2072                 }
2073             } else {
2074                 if ( !$skip ) {
2075
2076                     # leader
2077                     if ( @$tags[$i] eq "000" ) {
2078                         $xml .= "<leader>@$values[$i]</leader>\n";
2079                         $first = 1;
2080
2081                         # rest of the fixed fields
2082                     } elsif ( @$tags[$i] < 10 ) {
2083                         $xml .= "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2084                         $first = 1;
2085                     } else {
2086                         $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2087                         $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2088                         $first = 0;
2089                         $close_last_tag = 1;
2090                     }
2091                 }
2092             }
2093         } else {    # @$tags[$i] eq $prevtag
2094             if ( !$skip ) {
2095                 if ($first) {
2096                     my $str = ( $indicator->[$j] // q{} ) . '  '; # extra space prevents substr outside of string warn
2097                     my $ind1 = _default_ind_to_space( substr( $str, 0, 1 ) );
2098                     my $ind2 = _default_ind_to_space( substr( $str, 1, 1 ) );
2099                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2100                     $first = 0;
2101                     $close_last_tag = 1;
2102                 }
2103                 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2104             }
2105         }
2106         $prevtag = @$tags[$i];
2107     }
2108     $xml .= "</datafield>\n" if $close_last_tag;
2109     if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist ) {
2110
2111         #     warn "SETTING 100 for $auth_type";
2112         my $string = strftime( "%Y%m%d", localtime(time) );
2113
2114         # set 50 to position 26 is biblios, 13 if authorities
2115         my $pos = 26;
2116         $pos = 13 if $auth_type eq 'UNIMARCAUTH';
2117         $string = sprintf( "%-*s", 35, $string );
2118         substr( $string, $pos, 6, "50" );
2119         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2120         $xml .= "<subfield code=\"a\">$string</subfield>\n";
2121         $xml .= "</datafield>\n";
2122     }
2123     $xml .= "</record>\n";
2124     $xml .= MARC::File::XML::footer();
2125     return $xml;
2126 }
2127
2128 =head2 _default_ind_to_space
2129
2130 Passed what should be an indicator returns a space
2131 if its undefined or zero length
2132
2133 =cut
2134
2135 sub _default_ind_to_space {
2136     my $s = shift;
2137     if ( !defined $s || $s eq q{} ) {
2138         return ' ';
2139     }
2140     return $s;
2141 }
2142
2143 =head2 TransformHtmlToMarc
2144
2145     L<$record> = TransformHtmlToMarc(L<$cgi>)
2146     L<$cgi> is the CGI object which contains the values for subfields
2147     {
2148         'tag_010_indicator1_531951' ,
2149         'tag_010_indicator2_531951' ,
2150         'tag_010_code_a_531951_145735' ,
2151         'tag_010_subfield_a_531951_145735' ,
2152         'tag_200_indicator1_873510' ,
2153         'tag_200_indicator2_873510' ,
2154         'tag_200_code_a_873510_673465' ,
2155         'tag_200_subfield_a_873510_673465' ,
2156         'tag_200_code_b_873510_704318' ,
2157         'tag_200_subfield_b_873510_704318' ,
2158         'tag_200_code_e_873510_280822' ,
2159         'tag_200_subfield_e_873510_280822' ,
2160         'tag_200_code_f_873510_110730' ,
2161         'tag_200_subfield_f_873510_110730' ,
2162     }
2163     L<$record> is the MARC::Record object.
2164
2165 =cut
2166
2167 sub TransformHtmlToMarc {
2168     my ($cgi, $isbiblio) = @_;
2169
2170     my @params = $cgi->multi_param();
2171
2172     # explicitly turn on the UTF-8 flag for all
2173     # 'tag_' parameters to avoid incorrect character
2174     # conversion later on
2175     my $cgi_params = $cgi->Vars;
2176     foreach my $param_name ( keys %$cgi_params ) {
2177         if ( $param_name =~ /^tag_/ ) {
2178             my $param_value = $cgi_params->{$param_name};
2179             unless ( Encode::is_utf8( $param_value ) ) {
2180                 $cgi_params->{$param_name} = Encode::decode('UTF-8', $param_value );
2181             }
2182         }
2183     }
2184
2185     # creating a new record
2186     my $record = MARC::Record->new();
2187     my @fields;
2188     my ($biblionumbertagfield, $biblionumbertagsubfield) = (-1, -1);
2189     ($biblionumbertagfield, $biblionumbertagsubfield) =
2190         &GetMarcFromKohaField( "biblio.biblionumber", '' ) if $isbiblio;
2191 #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!
2192     for (my $i = 0; $params[$i]; $i++ ) {    # browse all CGI params
2193         my $param    = $params[$i];
2194         my $newfield = 0;
2195
2196         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
2197         if ( $param eq 'biblionumber' ) {
2198             if ( $biblionumbertagfield < 10 ) {
2199                 $newfield = MARC::Field->new( $biblionumbertagfield, scalar $cgi->param($param), );
2200             } else {
2201                 $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => scalar $cgi->param($param), );
2202             }
2203             push @fields, $newfield if ($newfield);
2204         } elsif ( $param =~ /^tag_(\d*)_indicator1_/ ) {    # new field start when having 'input name="..._indicator1_..."
2205             my $tag = $1;
2206
2207             my $ind1 = _default_ind_to_space( substr( $cgi->param($param), 0, 1 ) );
2208             my $ind2 = _default_ind_to_space( substr( $cgi->param( $params[ $i + 1 ] ), 0, 1 ) );
2209             $newfield = 0;
2210             my $j = $i + 2;
2211
2212             if ( $tag < 10 ) {                              # no code for theses fields
2213                                                             # in MARC editor, 000 contains the leader.
2214                 next if $tag == $biblionumbertagfield;
2215                 my $fval= $cgi->param($params[$j+1]);
2216                 if ( $tag eq '000' ) {
2217                     # Force a fake leader even if not provided to avoid crashing
2218                     # during decoding MARC record containing UTF-8 characters
2219                     $record->leader(
2220                         length( $fval ) == 24
2221                         ? $fval
2222                         : '     nam a22        4500'
2223                         )
2224                     ;
2225                     # between 001 and 009 (included)
2226                 } elsif ( $fval ne '' ) {
2227                     $newfield = MARC::Field->new( $tag, $fval, );
2228                 }
2229
2230                 # > 009, deal with subfields
2231             } else {
2232                 # browse subfields for this tag (reason for _code_ match)
2233                 while(defined $params[$j] && $params[$j] =~ /_code_/) {
2234                     last unless defined $params[$j+1];
2235                     $j += 2 and next
2236                         if $tag == $biblionumbertagfield and
2237                            $cgi->param($params[$j]) eq $biblionumbertagsubfield;
2238                     #if next param ne subfield, then it was probably empty
2239                     #try next param by incrementing j
2240                     if($params[$j+1]!~/_subfield_/) {$j++; next; }
2241                     my $fkey= $cgi->param($params[$j]);
2242                     my $fval= $cgi->param($params[$j+1]);
2243                     #check if subfield value not empty and field exists
2244                     if($fval ne '' && $newfield) {
2245                         $newfield->add_subfields( $fkey => $fval);
2246                     }
2247                     elsif($fval ne '') {
2248                         $newfield = MARC::Field->new( $tag, $ind1, $ind2, $fkey => $fval );
2249                     }
2250                     $j += 2;
2251                 } #end-of-while
2252                 $i= $j-1; #update i for outer loop accordingly
2253             }
2254             push @fields, $newfield if ($newfield);
2255         }
2256     }
2257
2258     @fields = sort { $a->tag() cmp $b->tag() } @fields;
2259     $record->append_fields(@fields);
2260     return $record;
2261 }
2262
2263 =head2 TransformMarcToKoha
2264
2265     $result = TransformMarcToKoha( $record, undef, $limit )
2266
2267 Extract data from a MARC bib record into a hashref representing
2268 Koha biblio, biblioitems, and items fields.
2269
2270 If passed an undefined record will log the error and return an empty
2271 hash_ref.
2272
2273 =cut
2274
2275 sub TransformMarcToKoha {
2276     my ( $record, $frameworkcode, $limit_table ) = @_;
2277     # FIXME  Parameter $frameworkcode is obsolete and will be removed
2278     $limit_table //= q{};
2279
2280     my $result = {};
2281     if (!defined $record) {
2282         carp('TransformMarcToKoha called with undefined record');
2283         return $result;
2284     }
2285
2286     my %tables = ( biblio => 1, biblioitems => 1, items => 1 );
2287     if( $limit_table eq 'items' ) {
2288         %tables = ( items => 1 );
2289     } elsif ( $limit_table eq 'no_items' ){
2290         %tables = ( biblio => 1, biblioitems => 1 );
2291     }
2292
2293     # The next call acknowledges Default as the authoritative framework
2294     # for Koha to MARC mappings.
2295     my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
2296     foreach my $kohafield ( keys %{ $mss } ) {
2297         my ( $table, $column ) = split /[.]/, $kohafield, 2;
2298         next unless $tables{$table};
2299         my $val = TransformMarcToKohaOneField( $kohafield, $record );
2300         next if !defined $val;
2301         my $key = _disambiguate( $table, $column );
2302         $result->{$key} = $val;
2303     }
2304     return $result;
2305 }
2306
2307 =head2 _disambiguate
2308
2309   $newkey = _disambiguate($table, $field);
2310
2311 This is a temporary hack to distinguish between the
2312 following sets of columns when using TransformMarcToKoha.
2313
2314   items.cn_source & biblioitems.cn_source
2315   items.cn_sort & biblioitems.cn_sort
2316
2317 Columns that are currently NOT distinguished (FIXME
2318 due to lack of time to fully test) are:
2319
2320   biblio.notes and biblioitems.notes
2321   biblionumber
2322   timestamp
2323   biblioitemnumber
2324
2325 FIXME - this is necessary because prefixing each column
2326 name with the table name would require changing lots
2327 of code and templates, and exposing more of the DB
2328 structure than is good to the UI templates, particularly
2329 since biblio and bibloitems may well merge in a future
2330 version.  In the future, it would also be good to 
2331 separate DB access and UI presentation field names
2332 more.
2333
2334 =cut
2335
2336 sub _disambiguate {
2337     my ( $table, $column ) = @_;
2338     if ( $column eq "cn_sort" or $column eq "cn_source" ) {
2339         return $table . '.' . $column;
2340     } else {
2341         return $column;
2342     }
2343
2344 }
2345
2346 =head2 TransformMarcToKohaOneField
2347
2348     $val = TransformMarcToKohaOneField( 'biblio.title', $marc );
2349
2350     Note: The authoritative Default framework is used implicitly.
2351
2352 =cut
2353
2354 sub TransformMarcToKohaOneField {
2355     my ( $kohafield, $marc ) = @_;
2356
2357     my ( @rv, $retval );
2358     my @mss = GetMarcSubfieldStructureFromKohaField($kohafield);
2359     foreach my $fldhash ( @mss ) {
2360         my $tag = $fldhash->{tagfield};
2361         my $sub = $fldhash->{tagsubfield};
2362         foreach my $fld ( $marc->field($tag) ) {
2363             if( $sub eq '@' || $fld->is_control_field ) {
2364                 push @rv, $fld->data if $fld->data;
2365             } else {
2366                 push @rv, grep { $_ } $fld->subfield($sub);
2367             }
2368         }
2369     }
2370     return unless @rv;
2371     $retval = join ' | ', uniq(@rv);
2372
2373     # Additional polishing for individual kohafields
2374     if( $kohafield =~ /copyrightdate|publicationyear/ ) {
2375         $retval = _adjust_pubyear( $retval );
2376     }
2377
2378     return $retval;
2379 }
2380
2381 =head2 _adjust_pubyear
2382
2383     Helper routine for TransformMarcToKohaOneField
2384
2385 =cut
2386
2387 sub _adjust_pubyear {
2388     my $retval = shift;
2389     # modify return value to keep only the 1st year found
2390     if( $retval =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
2391         $retval = $1;
2392     } elsif( $retval =~ m/(\d\d\d\d)/ && $1 > 0 ) {
2393         $retval = $1;
2394     } elsif( $retval =~ m/(?<year>\d{1,3})[.Xx?-]/ ) {
2395         # See also bug 24674: enough to look at one unknown year char like .Xx-?
2396         # At this point in code 1234? or 1234- already passed the earlier regex
2397         # Things like 2-, 1xx, 1??? are now converted to a four positions-year.
2398         $retval = $+{year} * ( 10 ** (4-length($+{year})) );
2399     } else {
2400         $retval = undef;
2401     }
2402     return $retval;
2403 }
2404
2405 =head2 CountItemsIssued
2406
2407     my $count = CountItemsIssued( $biblionumber );
2408
2409 =cut
2410
2411 sub CountItemsIssued {
2412     my ($biblionumber) = @_;
2413     my $dbh            = C4::Context->dbh;
2414     my $sth            = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
2415     $sth->execute($biblionumber);
2416     my $row = $sth->fetchrow_hashref();
2417     return $row->{'issuedCount'};
2418 }
2419
2420 =head2 ModZebra
2421
2422     ModZebra( $record_number, $op, $server );
2423
2424 $record_number is the authid or biblionumber we want to index
2425
2426 $op is the operation: specialUpdate or recordDelete
2427
2428 $server is authorityserver or biblioserver
2429
2430 =cut
2431
2432 sub ModZebra {
2433     my ( $record_number, $op, $server ) = @_;
2434     Koha::Logger->get->debug("ModZebra: updates requested for: $record_number $op $server");
2435     my $dbh = C4::Context->dbh;
2436
2437     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2438     # at the same time
2439     # replaced by a zebraqueue table, that is filled with ModZebra to run.
2440     # the table is emptied by rebuild_zebra.pl script (using the -z switch)
2441     my $check_sql = "SELECT COUNT(*) FROM zebraqueue
2442     WHERE server = ?
2443         AND   biblio_auth_number = ?
2444         AND   operation = ?
2445         AND   done = 0";
2446     my $check_sth = $dbh->prepare_cached($check_sql);
2447     $check_sth->execute( $server, $record_number, $op );
2448     my ($count) = $check_sth->fetchrow_array;
2449     $check_sth->finish();
2450     if ( $count == 0 ) {
2451         my $sth = $dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2452         $sth->execute( $record_number, $server, $op );
2453         $sth->finish;
2454     }
2455 }
2456
2457 =head2 EmbedItemsInMarcBiblio
2458
2459     EmbedItemsInMarcBiblio({
2460         marc_record  => $marc,
2461         biblionumber => $biblionumber,
2462         item_numbers => $itemnumbers,
2463         opac         => $opac });
2464
2465 Given a MARC::Record object containing a bib record,
2466 modify it to include the items attached to it as 9XX
2467 per the bib's MARC framework.
2468 if $itemnumbers is defined, only specified itemnumbers are embedded.
2469
2470 If $opac is true, then opac-relevant suppressions are included.
2471
2472 If opac filtering will be done, borcat should be passed to properly
2473 override if necessary.
2474
2475 =cut
2476
2477 sub EmbedItemsInMarcBiblio {
2478     my ($params) = @_;
2479     my ($marc, $biblionumber, $itemnumbers, $opac, $borcat);
2480     $marc = $params->{marc_record};
2481     if ( !$marc ) {
2482         carp 'EmbedItemsInMarcBiblio: No MARC record passed';
2483         return;
2484     }
2485     $biblionumber = $params->{biblionumber};
2486     $itemnumbers = $params->{item_numbers};
2487     $opac = $params->{opac};
2488     $borcat = $params->{borcat} // q{};
2489
2490     $itemnumbers = [] unless defined $itemnumbers;
2491
2492     my $frameworkcode = GetFrameworkCode($biblionumber);
2493     _strip_item_fields($marc, $frameworkcode);
2494
2495     # ... and embed the current items
2496     my $dbh = C4::Context->dbh;
2497     my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
2498     $sth->execute($biblionumber);
2499     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
2500
2501     my @item_fields; # Array holding the actual MARC data for items to be included.
2502     my @items;       # Array holding items which are both in the list (sitenumbers)
2503                      # and on this biblionumber
2504
2505     # Flag indicating if there is potential hiding.
2506     my $opachiddenitems = $opac
2507       && ( C4::Context->preference('OpacHiddenItems') !~ /^\s*$/ );
2508
2509     while ( my ($itemnumber) = $sth->fetchrow_array ) {
2510         next if @$itemnumbers and not grep { $_ == $itemnumber } @$itemnumbers;
2511         my $item;
2512         if ( $opachiddenitems ) {
2513             $item = Koha::Items->find($itemnumber);
2514             $item = $item ? $item->unblessed : undef;
2515         }
2516         push @items, { itemnumber => $itemnumber, item => $item };
2517     }
2518     my @items2pass = map { $_->{item} } @items;
2519     my @hiddenitems =
2520       $opachiddenitems
2521       ? C4::Items::GetHiddenItemnumbers({
2522             items  => \@items2pass,
2523             borcat => $borcat })
2524       : ();
2525     # Convert to a hash for quick searching
2526     my %hiddenitems = map { $_ => 1 } @hiddenitems;
2527     foreach my $itemnumber ( map { $_->{itemnumber} } @items ) {
2528         next if $hiddenitems{$itemnumber};
2529         my $item_marc = C4::Items::GetMarcItem( $biblionumber, $itemnumber );
2530         push @item_fields, $item_marc->field($itemtag);
2531     }
2532     $marc->append_fields(@item_fields);
2533 }
2534
2535 =head1 INTERNAL FUNCTIONS
2536
2537 =head2 _koha_marc_update_bib_ids
2538
2539
2540   _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
2541
2542 Internal function to add or update biblionumber and biblioitemnumber to
2543 the MARC XML.
2544
2545 =cut
2546
2547 sub _koha_marc_update_bib_ids {
2548     my ( $record, $frameworkcode, $biblionumber, $biblioitemnumber ) = @_;
2549
2550     my ( $biblio_tag,     $biblio_subfield )     = GetMarcFromKohaField( "biblio.biblionumber" );
2551     die qq{No biblionumber tag for framework "$frameworkcode"} unless $biblio_tag;
2552     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber" );
2553     die qq{No biblioitemnumber tag for framework "$frameworkcode"} unless $biblioitem_tag;
2554
2555     if ( $biblio_tag < 10 ) {
2556         C4::Biblio::UpsertMarcControlField( $record, $biblio_tag, $biblionumber );
2557     } else {
2558         C4::Biblio::UpsertMarcSubfield($record, $biblio_tag, $biblio_subfield, $biblionumber);
2559     }
2560     if ( $biblioitem_tag < 10 ) {
2561         C4::Biblio::UpsertMarcControlField( $record, $biblioitem_tag, $biblioitemnumber );
2562     } else {
2563         C4::Biblio::UpsertMarcSubfield($record, $biblioitem_tag, $biblioitem_subfield, $biblioitemnumber);
2564     }
2565 }
2566
2567 =head2 _koha_marc_update_biblioitem_cn_sort
2568
2569   _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
2570
2571 Given a MARC bib record and the biblioitem hash, update the
2572 subfield that contains a copy of the value of biblioitems.cn_sort.
2573
2574 =cut
2575
2576 sub _koha_marc_update_biblioitem_cn_sort {
2577     my $marc          = shift;
2578     my $biblioitem    = shift;
2579     my $frameworkcode = shift;
2580
2581     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.cn_sort" );
2582     return unless $biblioitem_tag;
2583
2584     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2585
2586     if ( my $field = $marc->field($biblioitem_tag) ) {
2587         $field->delete_subfield( code => $biblioitem_subfield );
2588         if ( $cn_sort ne '' ) {
2589             $field->add_subfields( $biblioitem_subfield => $cn_sort );
2590         }
2591     } else {
2592
2593         # if we get here, no biblioitem tag is present in the MARC record, so
2594         # we'll create it if $cn_sort is not empty -- this would be
2595         # an odd combination of events, however
2596         if ($cn_sort) {
2597             $marc->insert_grouped_field( MARC::Field->new( $biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort ) );
2598         }
2599     }
2600 }
2601
2602 =head2 _koha_modify_biblio
2603
2604   my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
2605
2606 Internal function for updating the biblio table
2607
2608 =cut
2609
2610 sub _koha_modify_biblio {
2611     my ( $dbh, $biblio, $frameworkcode ) = @_;
2612     my $error;
2613
2614     my $query = "
2615         UPDATE biblio
2616         SET    frameworkcode = ?,
2617                author = ?,
2618                title = ?,
2619                subtitle = ?,
2620                medium = ?,
2621                part_number = ?,
2622                part_name = ?,
2623                unititle = ?,
2624                notes = ?,
2625                serial = ?,
2626                seriestitle = ?,
2627                copyrightdate = ?,
2628                abstract = ?
2629         WHERE  biblionumber = ?
2630         "
2631       ;
2632     my $sth = $dbh->prepare($query);
2633
2634     $sth->execute(
2635         $frameworkcode,        $biblio->{'author'},      $biblio->{'title'},       $biblio->{'subtitle'},
2636         $biblio->{'medium'},   $biblio->{'part_number'}, $biblio->{'part_name'},   $biblio->{'unititle'},
2637         $biblio->{'notes'},    $biblio->{'serial'},      $biblio->{'seriestitle'}, $biblio->{'copyrightdate'} ? int($biblio->{'copyrightdate'}) : undef,
2638         $biblio->{'abstract'}, $biblio->{'biblionumber'}
2639     ) if $biblio->{'biblionumber'};
2640
2641     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
2642         $error .= "ERROR in _koha_modify_biblio $query" . $dbh->errstr;
2643         warn $error;
2644     }
2645     return ( $biblio->{'biblionumber'}, $error );
2646 }
2647
2648 =head2 _koha_modify_biblioitem_nonmarc
2649
2650   my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
2651
2652 =cut
2653
2654 sub _koha_modify_biblioitem_nonmarc {
2655     my ( $dbh, $biblioitem ) = @_;
2656     my $error;
2657
2658     # re-calculate the cn_sort, it may have changed
2659     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2660
2661     my $query = "UPDATE biblioitems 
2662     SET biblionumber    = ?,
2663         volume          = ?,
2664         number          = ?,
2665         itemtype        = ?,
2666         isbn            = ?,
2667         issn            = ?,
2668         publicationyear = ?,
2669         publishercode   = ?,
2670         volumedate      = ?,
2671         volumedesc      = ?,
2672         collectiontitle = ?,
2673         collectionissn  = ?,
2674         collectionvolume= ?,
2675         editionstatement= ?,
2676         editionresponsibility = ?,
2677         illus           = ?,
2678         pages           = ?,
2679         notes           = ?,
2680         size            = ?,
2681         place           = ?,
2682         lccn            = ?,
2683         url             = ?,
2684         cn_source       = ?,
2685         cn_class        = ?,
2686         cn_item         = ?,
2687         cn_suffix       = ?,
2688         cn_sort         = ?,
2689         totalissues     = ?,
2690         ean             = ?,
2691         agerestriction  = ?
2692         where biblioitemnumber = ?
2693         ";
2694     my $sth = $dbh->prepare($query);
2695     $sth->execute(
2696         $biblioitem->{'biblionumber'},     $biblioitem->{'volume'},           $biblioitem->{'number'},                $biblioitem->{'itemtype'},
2697         $biblioitem->{'isbn'},             $biblioitem->{'issn'},             $biblioitem->{'publicationyear'},       $biblioitem->{'publishercode'},
2698         $biblioitem->{'volumedate'},       $biblioitem->{'volumedesc'},       $biblioitem->{'collectiontitle'},       $biblioitem->{'collectionissn'},
2699         $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
2700         $biblioitem->{'pages'},            $biblioitem->{'bnotes'},           $biblioitem->{'size'},                  $biblioitem->{'place'},
2701         $biblioitem->{'lccn'},             $biblioitem->{'url'},              $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'},
2702         $biblioitem->{'cn_item'},          $biblioitem->{'cn_suffix'},        $cn_sort,                               $biblioitem->{'totalissues'},
2703         $biblioitem->{'ean'},              $biblioitem->{'agerestriction'},   $biblioitem->{'biblioitemnumber'}
2704     );
2705     if ( $dbh->errstr ) {
2706         $error .= "ERROR in _koha_modify_biblioitem_nonmarc $query" . $dbh->errstr;
2707         warn $error;
2708     }
2709     return ( $biblioitem->{'biblioitemnumber'}, $error );
2710 }
2711
2712 =head2 _koha_delete_biblio
2713
2714   $error = _koha_delete_biblio($dbh,$biblionumber);
2715
2716 Internal sub for deleting from biblio table -- also saves to deletedbiblio
2717
2718 C<$dbh> - the database handle
2719
2720 C<$biblionumber> - the biblionumber of the biblio to be deleted
2721
2722 =cut
2723
2724 # FIXME: add error handling
2725
2726 sub _koha_delete_biblio {
2727     my ( $dbh, $biblionumber ) = @_;
2728
2729     # get all the data for this biblio
2730     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
2731     $sth->execute($biblionumber);
2732
2733     # FIXME There is a transaction in _koha_delete_biblio_metadata
2734     # But actually all the following should be done inside a single transaction
2735     if ( my $data = $sth->fetchrow_hashref ) {
2736
2737         # save the record in deletedbiblio
2738         # find the fields to save
2739         my $query = "INSERT INTO deletedbiblio SET ";
2740         my @bind  = ();
2741         foreach my $temp ( keys %$data ) {
2742             $query .= "$temp = ?,";
2743             push( @bind, $data->{$temp} );
2744         }
2745
2746         # replace the last , by ",?)"
2747         $query =~ s/\,$//;
2748         my $bkup_sth = $dbh->prepare($query);
2749         $bkup_sth->execute(@bind);
2750         $bkup_sth->finish;
2751
2752         _koha_delete_biblio_metadata( $biblionumber );
2753
2754         # delete the biblio
2755         my $sth2 = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
2756         $sth2->execute($biblionumber);
2757         # update the timestamp (Bugzilla 7146)
2758         $sth2= $dbh->prepare("UPDATE deletedbiblio SET timestamp=NOW() WHERE biblionumber=?");
2759         $sth2->execute($biblionumber);
2760         $sth2->finish;
2761     }
2762     $sth->finish;
2763     return;
2764 }
2765
2766 =head2 _koha_delete_biblioitems
2767
2768   $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
2769
2770 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
2771
2772 C<$dbh> - the database handle
2773 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
2774
2775 =cut
2776
2777 # FIXME: add error handling
2778
2779 sub _koha_delete_biblioitems {
2780     my ( $dbh, $biblioitemnumber ) = @_;
2781
2782     # get all the data for this biblioitem
2783     my $sth = $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
2784     $sth->execute($biblioitemnumber);
2785
2786     if ( my $data = $sth->fetchrow_hashref ) {
2787
2788         # save the record in deletedbiblioitems
2789         # find the fields to save
2790         my $query = "INSERT INTO deletedbiblioitems SET ";
2791         my @bind  = ();
2792         foreach my $temp ( keys %$data ) {
2793             $query .= "$temp = ?,";
2794             push( @bind, $data->{$temp} );
2795         }
2796
2797         # replace the last , by ",?)"
2798         $query =~ s/\,$//;
2799         my $bkup_sth = $dbh->prepare($query);
2800         $bkup_sth->execute(@bind);
2801         $bkup_sth->finish;
2802
2803         # delete the biblioitem
2804         my $sth2 = $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
2805         $sth2->execute($biblioitemnumber);
2806         # update the timestamp (Bugzilla 7146)
2807         $sth2= $dbh->prepare("UPDATE deletedbiblioitems SET timestamp=NOW() WHERE biblioitemnumber=?");
2808         $sth2->execute($biblioitemnumber);
2809         $sth2->finish;
2810     }
2811     $sth->finish;
2812     return;
2813 }
2814
2815 =head2 _koha_delete_biblio_metadata
2816
2817   $error = _koha_delete_biblio_metadata($biblionumber);
2818
2819 C<$biblionumber> - the biblionumber of the biblio metadata to be deleted
2820
2821 =cut
2822
2823 sub _koha_delete_biblio_metadata {
2824     my ($biblionumber) = @_;
2825
2826     my $dbh    = C4::Context->dbh;
2827     my $schema = Koha::Database->new->schema;
2828     $schema->txn_do(
2829         sub {
2830             $dbh->do( q|
2831                 INSERT INTO deletedbiblio_metadata (biblionumber, format, `schema`, metadata)
2832                 SELECT biblionumber, format, `schema`, metadata FROM biblio_metadata WHERE biblionumber=?
2833             |,  undef, $biblionumber );
2834             $dbh->do( q|DELETE FROM biblio_metadata WHERE biblionumber=?|,
2835                 undef, $biblionumber );
2836         }
2837     );
2838 }
2839
2840 =head1 UNEXPORTED FUNCTIONS
2841
2842 =head2 ModBiblioMarc
2843
2844   ModBiblioMarc($newrec,$biblionumber);
2845
2846 Add MARC XML data for a biblio to koha
2847
2848 Function exported, but should NOT be used, unless you really know what you're doing
2849
2850 =cut
2851
2852 sub ModBiblioMarc {
2853     # pass the MARC::Record to this function, and it will create the records in
2854     # the marcxml field
2855     my ( $record, $biblionumber ) = @_;
2856     if ( !$record ) {
2857         carp 'ModBiblioMarc passed an undefined record';
2858         return;
2859     }
2860
2861     # Clone record as it gets modified
2862     $record = $record->clone();
2863     my $dbh    = C4::Context->dbh;
2864     my @fields = $record->fields();
2865     my $encoding = C4::Context->preference("marcflavour");
2866
2867     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
2868     if ( $encoding eq "UNIMARC" ) {
2869         my $defaultlanguage = C4::Context->preference("UNIMARCField100Language");
2870         $defaultlanguage = "fre" if (!$defaultlanguage || length($defaultlanguage) != 3);
2871         my $string = $record->subfield( 100, "a" );
2872         if ( ($string) && ( length( $record->subfield( 100, "a" ) ) == 36 ) ) {
2873             my $f100 = $record->field(100);
2874             $record->delete_field($f100);
2875         } else {
2876             $string = POSIX::strftime( "%Y%m%d", localtime );
2877             $string =~ s/\-//g;
2878             $string = sprintf( "%-*s", 35, $string );
2879             substr ( $string, 22, 3, $defaultlanguage);
2880         }
2881         substr( $string, 25, 3, "y50" );
2882         unless ( $record->subfield( 100, "a" ) ) {
2883             $record->insert_fields_ordered( MARC::Field->new( 100, "", "", "a" => $string ) );
2884         }
2885     }
2886
2887     #enhancement 5374: update transaction date (005) for marc21/unimarc
2888     if($encoding =~ /MARC21|UNIMARC/) {
2889       my @a= (localtime) [5,4,3,2,1,0]; $a[0]+=1900; $a[1]++;
2890         # YY MM DD HH MM SS (update year and month)
2891       my $f005= $record->field('005');
2892       $f005->update(sprintf("%4d%02d%02d%02d%02d%04.1f",@a)) if $f005;
2893     }
2894
2895     my $metadata = {
2896         biblionumber => $biblionumber,
2897         format       => 'marcxml',
2898         schema       => C4::Context->preference('marcflavour'),
2899     };
2900     $record->as_usmarc; # Bug 20126/10455 This triggers field length calculation
2901
2902     my $m_rs = Koha::Biblio::Metadatas->find($metadata) //
2903         Koha::Biblio::Metadata->new($metadata);
2904
2905     my $userenv = C4::Context->userenv;
2906     if ($userenv) {
2907         my $borrowernumber = $userenv->{number};
2908         my $borrowername = join ' ', map { $_ // q{} } @$userenv{qw(firstname surname)};
2909         unless ($m_rs->in_storage) {
2910             Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForCreatorId'), $borrowernumber);
2911             Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForCreatorName'), $borrowername);
2912         }
2913         Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForModifierId'), $borrowernumber);
2914         Koha::Util::MARC::set_marc_field($record, C4::Context->preference('MarcFieldForModifierName'), $borrowername);
2915     }
2916
2917     $m_rs->metadata( $record->as_xml_record($encoding) );
2918     $m_rs->store;
2919
2920     my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
2921     $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
2922
2923     return $biblionumber;
2924 }
2925
2926 =head2 prepare_host_field
2927
2928 $marcfield = prepare_host_field( $hostbiblioitem, $marcflavour );
2929 Generate the host item entry for an analytic child entry
2930
2931 =cut
2932
2933 sub prepare_host_field {
2934     my ( $hostbiblio, $marcflavour ) = @_;
2935     $marcflavour ||= C4::Context->preference('marcflavour');
2936     my $host = GetMarcBiblio({ biblionumber => $hostbiblio });
2937     # unfortunately as_string does not 'do the right thing'
2938     # if field returns undef
2939     my %sfd;
2940     my $field;
2941     my $host_field;
2942     if ( $marcflavour eq 'MARC21' ) {
2943         if ( $field = $host->field('100') || $host->field('110') || $host->field('11') ) {
2944             my $s = $field->as_string('ab');
2945             if ($s) {
2946                 $sfd{a} = $s;
2947             }
2948         }
2949         if ( $field = $host->field('245') ) {
2950             my $s = $field->as_string('a');
2951             if ($s) {
2952                 $sfd{t} = $s;
2953             }
2954         }
2955         if ( $field = $host->field('260') ) {
2956             my $s = $field->as_string('abc');
2957             if ($s) {
2958                 $sfd{d} = $s;
2959             }
2960         }
2961         if ( $field = $host->field('240') ) {
2962             my $s = $field->as_string();
2963             if ($s) {
2964                 $sfd{b} = $s;
2965             }
2966         }
2967         if ( $field = $host->field('022') ) {
2968             my $s = $field->as_string('a');
2969             if ($s) {
2970                 $sfd{x} = $s;
2971             }
2972         }
2973         if ( $field = $host->field('020') ) {
2974             my $s = $field->as_string('a');
2975             if ($s) {
2976                 $sfd{z} = $s;
2977             }
2978         }
2979         if ( $field = $host->field('001') ) {
2980             $sfd{w} = $field->data(),;
2981         }
2982         $host_field = MARC::Field->new( 773, '0', ' ', %sfd );
2983         return $host_field;
2984     }
2985     elsif ( $marcflavour eq 'UNIMARC' ) {
2986         #author
2987         if ( $field = $host->field('700') || $host->field('710') || $host->field('720') ) {
2988             my $s = $field->as_string('ab');
2989             if ($s) {
2990                 $sfd{a} = $s;
2991             }
2992         }
2993         #title
2994         if ( $field = $host->field('200') ) {
2995             my $s = $field->as_string('a');
2996             if ($s) {
2997                 $sfd{t} = $s;
2998             }
2999         }
3000         #place of publicaton
3001         if ( $field = $host->field('210') ) {
3002             my $s = $field->as_string('a');
3003             if ($s) {
3004                 $sfd{c} = $s;
3005             }
3006         }
3007         #date of publication
3008         if ( $field = $host->field('210') ) {
3009             my $s = $field->as_string('d');
3010             if ($s) {
3011                 $sfd{d} = $s;
3012             }
3013         }
3014         #edition statement
3015         if ( $field = $host->field('205') ) {
3016             my $s = $field->as_string();
3017             if ($s) {
3018                 $sfd{e} = $s;
3019             }
3020         }
3021         #URL
3022         if ( $field = $host->field('856') ) {
3023             my $s = $field->as_string('u');
3024             if ($s) {
3025                 $sfd{u} = $s;
3026             }
3027         }
3028         #ISSN
3029         if ( $field = $host->field('011') ) {
3030             my $s = $field->as_string('a');
3031             if ($s) {
3032                 $sfd{x} = $s;
3033             }
3034         }
3035         #ISBN
3036         if ( $field = $host->field('010') ) {
3037             my $s = $field->as_string('a');
3038             if ($s) {
3039                 $sfd{y} = $s;
3040             }
3041         }
3042         if ( $field = $host->field('001') ) {
3043             $sfd{0} = $field->data(),;
3044         }
3045         $host_field = MARC::Field->new( 461, '0', ' ', %sfd );
3046         return $host_field;
3047     }
3048     return;
3049 }
3050
3051
3052 =head2 UpdateTotalIssues
3053
3054   UpdateTotalIssues($biblionumber, $increase, [$value])
3055
3056 Update the total issue count for a particular bib record.
3057
3058 =over 4
3059
3060 =item C<$biblionumber> is the biblionumber of the bib to update
3061
3062 =item C<$increase> is the amount to increase (or decrease) the total issues count by
3063
3064 =item C<$value> is the absolute value that total issues count should be set to. If provided, C<$increase> is ignored.
3065
3066 =back
3067
3068 =cut
3069
3070 sub UpdateTotalIssues {
3071     my ($biblionumber, $increase, $value) = @_;
3072     my $totalissues;
3073
3074     my $record = GetMarcBiblio({ biblionumber => $biblionumber });
3075     unless ($record) {
3076         carp "UpdateTotalIssues could not get biblio record";
3077         return;
3078     }
3079     my $biblio = Koha::Biblios->find( $biblionumber );
3080     unless ($biblio) {
3081         carp "UpdateTotalIssues could not get datas of biblio";
3082         return;
3083     }
3084     my $biblioitem = $biblio->biblioitem;
3085     my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField( 'biblioitems.totalissues' );
3086     unless ($totalissuestag) {
3087         return 1; # There is nothing to do
3088     }
3089
3090     if (defined $value) {
3091         $totalissues = $value;
3092     } else {
3093         $totalissues = $biblioitem->totalissues + $increase;
3094     }
3095
3096      my $field = $record->field($totalissuestag);
3097      if (defined $field) {
3098          $field->update( $totalissuessubfield => $totalissues );
3099      } else {
3100          $field = MARC::Field->new($totalissuestag, '0', '0',
3101                  $totalissuessubfield => $totalissues);
3102          $record->insert_grouped_field($field);
3103      }
3104
3105      return ModBiblio($record, $biblionumber, $biblio->frameworkcode);
3106 }
3107
3108 =head2 RemoveAllNsb
3109
3110     &RemoveAllNsb($record);
3111
3112 Removes all nsb/nse chars from a record
3113
3114 =cut
3115
3116 sub RemoveAllNsb {
3117     my $record = shift;
3118     if (!$record) {
3119         carp 'RemoveAllNsb called with undefined record';
3120         return;
3121     }
3122
3123     SetUTF8Flag($record);
3124
3125     foreach my $field ($record->fields()) {
3126         if ($field->is_control_field()) {
3127             $field->update(nsb_clean($field->data()));
3128         } else {
3129             my @subfields = $field->subfields();
3130             my @new_subfields;
3131             foreach my $subfield (@subfields) {
3132                 push @new_subfields, $subfield->[0] => nsb_clean($subfield->[1]);
3133             }
3134             if (scalar(@new_subfields) > 0) {
3135                 my $new_field;
3136                 eval {
3137                     $new_field = MARC::Field->new(
3138                         $field->tag(),
3139                         $field->indicator(1),
3140                         $field->indicator(2),
3141                         @new_subfields
3142                     );
3143                 };
3144                 if ($@) {
3145                     warn "error in RemoveAllNsb : $@";
3146                 } else {
3147                     $field->replace_with($new_field);
3148                 }
3149             }
3150         }
3151     }
3152
3153     return $record;
3154 }
3155
3156 =head2 ApplyMarcOverlayRules
3157
3158     my $record = ApplyMarcOverlayRules($params)
3159
3160 Applies marc merge rules to a record.
3161
3162 C<$params> is expected to be a hashref with below keys defined.
3163
3164 =over 4
3165
3166 =item C<biblionumber>
3167 biblionumber of old record
3168
3169 =item C<record>
3170 Incoming record that will be merged with old record
3171
3172 =item C<overlay_context>
3173 hashref containing at least one context module and filter value on
3174 the form {module => filter, ...}.
3175
3176 =back
3177
3178 Returns:
3179
3180 =over 4
3181
3182 =item C<$record>
3183
3184 Merged MARC record based with merge rules for C<context> applied. If no old
3185 record for C<biblionumber> can be found, C<record> is returned unchanged.
3186 Default action when no matching context is found to return C<record> unchanged.
3187 If no rules are found for a certain field tag the default is to overwrite with
3188 fields with this field tag from C<record>.
3189
3190 =back
3191
3192 =cut
3193
3194 sub ApplyMarcOverlayRules {
3195     my ($params) = @_;
3196     my $biblionumber = $params->{biblionumber};
3197     my $incoming_record = $params->{record};
3198
3199     if (!$biblionumber) {
3200         carp 'ApplyMarcOverlayRules called on undefined biblionumber';
3201         return;
3202     }
3203     if (!$incoming_record) {
3204         carp 'ApplyMarcOverlayRules called on undefined record';
3205         return;
3206     }
3207     my $old_record = GetMarcBiblio({ biblionumber => $biblionumber });
3208
3209     # Skip overlay rules if called with no context
3210     if ($old_record && defined $params->{overlay_context}) {
3211         return Koha::MarcOverlayRules->merge_records($old_record, $incoming_record, $params->{overlay_context});
3212     }
3213     return $incoming_record;
3214 }
3215
3216 =head2 _after_biblio_action_hooks
3217
3218 Helper method that takes care of calling all plugin hooks
3219
3220 =cut
3221
3222 sub _after_biblio_action_hooks {
3223     my ( $args ) = @_;
3224
3225     my $biblio_id = $args->{biblio_id};
3226     my $action    = $args->{action};
3227
3228     my $biblio = Koha::Biblios->find( $biblio_id );
3229     Koha::Plugins->call(
3230         'after_biblio_action',
3231         {
3232             action    => $action,
3233             biblio    => $biblio,
3234             biblio_id => $biblio_id,
3235         }
3236     );
3237 }
3238
3239 1;
3240
3241 __END__
3242
3243 =head1 AUTHOR
3244
3245 Koha Development Team <http://koha-community.org/>
3246
3247 Paul POULAIN paul.poulain@free.fr
3248
3249 Joshua Ferraro jmf@liblime.com
3250
3251 =cut