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