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