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