Bug 17189: Add the ability to define several memcached namespaces - replace existing...
[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::Caches->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::Caches->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 GetFrameworkCode
2215
2216   $frameworkcode = GetFrameworkCode( $biblionumber )
2217
2218 =cut
2219
2220 sub GetFrameworkCode {
2221     my ($biblionumber) = @_;
2222     my $dbh            = C4::Context->dbh;
2223     my $sth            = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
2224     $sth->execute($biblionumber);
2225     my ($frameworkcode) = $sth->fetchrow;
2226     return $frameworkcode;
2227 }
2228
2229 =head2 TransformKohaToMarc
2230
2231     $record = TransformKohaToMarc( $hash )
2232
2233 This function builds partial MARC::Record from a hash
2234 Hash entries can be from biblio or biblioitems.
2235
2236 This function is called in acquisition module, to create a basic catalogue
2237 entry from user entry
2238
2239 =cut
2240
2241
2242 sub TransformKohaToMarc {
2243     my $hash = shift;
2244     my $record = MARC::Record->new();
2245     SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
2246     # FIXME Do not we want to get the marc subfield structure for the biblio framework?
2247     my $mss = GetMarcSubfieldStructure();
2248     my $tag_hr = {};
2249     while ( my ($kohafield, $value) = each %$hash ) {
2250         next unless exists $mss->{$kohafield};
2251         next unless $mss->{$kohafield};
2252         my $tagfield    = $mss->{$kohafield}{tagfield} . '';
2253         my $tagsubfield = $mss->{$kohafield}{tagsubfield};
2254         foreach my $value ( split(/\s?\|\s?/, $value, -1) ) {
2255             next if $value eq '';
2256             $tag_hr->{$tagfield} //= [];
2257             push @{$tag_hr->{$tagfield}}, [($tagsubfield, $value)];
2258         }
2259     }
2260     foreach my $tag (sort keys %$tag_hr) {
2261         my @sfl = @{$tag_hr->{$tag}};
2262         @sfl = sort { $a->[0] cmp $b->[0]; } @sfl;
2263         @sfl = map { @{$_}; } @sfl;
2264         $record->insert_fields_ordered(
2265             MARC::Field->new($tag, " ", " ", @sfl)
2266         );
2267     }
2268     return $record;
2269 }
2270
2271 =head2 PrepHostMarcField
2272
2273     $hostfield = PrepHostMarcField ( $hostbiblionumber,$hostitemnumber,$marcflavour )
2274
2275 This function returns a host field populated with data from the host record, the field can then be added to an analytical record
2276
2277 =cut
2278
2279 sub PrepHostMarcField {
2280     my ($hostbiblionumber,$hostitemnumber, $marcflavour) = @_;
2281     $marcflavour ||="MARC21";
2282     
2283     require C4::Items;
2284     my $hostrecord = GetMarcBiblio($hostbiblionumber);
2285         my $item = C4::Items::GetItem($hostitemnumber);
2286         
2287         my $hostmarcfield;
2288     if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
2289         
2290         #main entry
2291         my $mainentry;
2292         if ($hostrecord->subfield('100','a')){
2293             $mainentry = $hostrecord->subfield('100','a');
2294         } elsif ($hostrecord->subfield('110','a')){
2295             $mainentry = $hostrecord->subfield('110','a');
2296         } else {
2297             $mainentry = $hostrecord->subfield('111','a');
2298         }
2299         
2300         # qualification info
2301         my $qualinfo;
2302         if (my $field260 = $hostrecord->field('260')){
2303             $qualinfo =  $field260->as_string( 'abc' );
2304         }
2305         
2306
2307         #other fields
2308         my $ed = $hostrecord->subfield('250','a');
2309         my $barcode = $item->{'barcode'};
2310         my $title = $hostrecord->subfield('245','a');
2311
2312         # record control number, 001 with 003 and prefix
2313         my $recctrlno;
2314         if ($hostrecord->field('001')){
2315             $recctrlno = $hostrecord->field('001')->data();
2316             if ($hostrecord->field('003')){
2317                 $recctrlno = '('.$hostrecord->field('003')->data().')'.$recctrlno;
2318             }
2319         }
2320
2321         # issn/isbn
2322         my $issn = $hostrecord->subfield('022','a');
2323         my $isbn = $hostrecord->subfield('020','a');
2324
2325
2326         $hostmarcfield = MARC::Field->new(
2327                 773, '0', '',
2328                 '0' => $hostbiblionumber,
2329                 '9' => $hostitemnumber,
2330                 'a' => $mainentry,
2331                 'b' => $ed,
2332                 'd' => $qualinfo,
2333                 'o' => $barcode,
2334                 't' => $title,
2335                 'w' => $recctrlno,
2336                 'x' => $issn,
2337                 'z' => $isbn
2338                 );
2339     } elsif ($marcflavour eq "UNIMARC") {
2340         $hostmarcfield = MARC::Field->new(
2341             461, '', '',
2342             '0' => $hostbiblionumber,
2343             't' => $hostrecord->subfield('200','a'), 
2344             '9' => $hostitemnumber
2345         );      
2346     };
2347
2348     return $hostmarcfield;
2349 }
2350
2351 =head2 TransformHtmlToXml
2352
2353   $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, 
2354                              $ind_tag, $auth_type )
2355
2356 $auth_type contains :
2357
2358 =over
2359
2360 =item - nothing : rebuild a biblio. In UNIMARC the encoding is in 100$a pos 26/27
2361
2362 =item - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
2363
2364 =item - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
2365
2366 =back
2367
2368 =cut
2369
2370 sub TransformHtmlToXml {
2371     my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
2372     # NOTE: The parameter $ind_tag is NOT USED -- BZ 11247
2373
2374     my $xml = MARC::File::XML::header('UTF-8');
2375     $xml .= "<record>\n";
2376     $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
2377     MARC::File::XML->default_record_format($auth_type);
2378
2379     # in UNIMARC, field 100 contains the encoding
2380     # check that there is one, otherwise the
2381     # MARC::Record->new_from_xml will fail (and Koha will die)
2382     my $unimarc_and_100_exist = 0;
2383     $unimarc_and_100_exist = 1 if $auth_type eq 'ITEM';    # if we rebuild an item, no need of a 100 field
2384     my $prevvalue;
2385     my $prevtag = -1;
2386     my $first   = 1;
2387     my $j       = -1;
2388     for ( my $i = 0 ; $i < @$tags ; $i++ ) {
2389
2390         if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a" ) {
2391
2392             # if we have a 100 field and it's values are not correct, skip them.
2393             # if we don't have any valid 100 field, we will create a default one at the end
2394             my $enc = substr( @$values[$i], 26, 2 );
2395             if ( $enc eq '01' or $enc eq '50' or $enc eq '03' ) {
2396                 $unimarc_and_100_exist = 1;
2397             } else {
2398                 next;
2399             }
2400         }
2401         @$values[$i] =~ s/&/&amp;/g;
2402         @$values[$i] =~ s/</&lt;/g;
2403         @$values[$i] =~ s/>/&gt;/g;
2404         @$values[$i] =~ s/"/&quot;/g;
2405         @$values[$i] =~ s/'/&apos;/g;
2406
2407         if ( ( @$tags[$i] ne $prevtag ) ) {
2408             $j++ unless ( @$tags[$i] eq "" );
2409             my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) };
2410             my $indicator2 = eval { substr( @$indicator[$j], 1, 1 ) };
2411             my $ind1       = _default_ind_to_space($indicator1);
2412             my $ind2;
2413             if ( @$indicator[$j] ) {
2414                 $ind2 = _default_ind_to_space($indicator2);
2415             } else {
2416                 warn "Indicator in @$tags[$i] is empty";
2417                 $ind2 = " ";
2418             }
2419             if ( !$first ) {
2420                 $xml .= "</datafield>\n";
2421                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
2422                     && ( @$values[$i] ne "" ) ) {
2423                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2424                     $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2425                     $first = 0;
2426                 } else {
2427                     $first = 1;
2428                 }
2429             } else {
2430                 if ( @$values[$i] ne "" ) {
2431
2432                     # leader
2433                     if ( @$tags[$i] eq "000" ) {
2434                         $xml .= "<leader>@$values[$i]</leader>\n";
2435                         $first = 1;
2436
2437                         # rest of the fixed fields
2438                     } elsif ( @$tags[$i] < 10 ) {
2439                         $xml .= "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2440                         $first = 1;
2441                     } else {
2442                         $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2443                         $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2444                         $first = 0;
2445                     }
2446                 }
2447             }
2448         } else {    # @$tags[$i] eq $prevtag
2449             my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) };
2450             my $indicator2 = eval { substr( @$indicator[$j], 1, 1 ) };
2451             my $ind1       = _default_ind_to_space($indicator1);
2452             my $ind2;
2453             if ( @$indicator[$j] ) {
2454                 $ind2 = _default_ind_to_space($indicator2);
2455             } else {
2456                 warn "Indicator in @$tags[$i] is empty";
2457                 $ind2 = " ";
2458             }
2459             if ( @$values[$i] eq "" ) {
2460             } else {
2461                 if ($first) {
2462                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2463                     $first = 0;
2464                 }
2465                 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2466             }
2467         }
2468         $prevtag = @$tags[$i];
2469     }
2470     $xml .= "</datafield>\n" if $xml =~ m/<datafield/;
2471     if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist ) {
2472
2473         #     warn "SETTING 100 for $auth_type";
2474         my $string = strftime( "%Y%m%d", localtime(time) );
2475
2476         # set 50 to position 26 is biblios, 13 if authorities
2477         my $pos = 26;
2478         $pos = 13 if $auth_type eq 'UNIMARCAUTH';
2479         $string = sprintf( "%-*s", 35, $string );
2480         substr( $string, $pos, 6, "50" );
2481         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2482         $xml .= "<subfield code=\"a\">$string</subfield>\n";
2483         $xml .= "</datafield>\n";
2484     }
2485     $xml .= "</record>\n";
2486     $xml .= MARC::File::XML::footer();
2487     return $xml;
2488 }
2489
2490 =head2 _default_ind_to_space
2491
2492 Passed what should be an indicator returns a space
2493 if its undefined or zero length
2494
2495 =cut
2496
2497 sub _default_ind_to_space {
2498     my $s = shift;
2499     if ( !defined $s || $s eq q{} ) {
2500         return ' ';
2501     }
2502     return $s;
2503 }
2504
2505 =head2 TransformHtmlToMarc
2506
2507     L<$record> = TransformHtmlToMarc(L<$cgi>)
2508     L<$cgi> is the CGI object which containts the values for subfields
2509     {
2510         'tag_010_indicator1_531951' ,
2511         'tag_010_indicator2_531951' ,
2512         'tag_010_code_a_531951_145735' ,
2513         'tag_010_subfield_a_531951_145735' ,
2514         'tag_200_indicator1_873510' ,
2515         'tag_200_indicator2_873510' ,
2516         'tag_200_code_a_873510_673465' ,
2517         'tag_200_subfield_a_873510_673465' ,
2518         'tag_200_code_b_873510_704318' ,
2519         'tag_200_subfield_b_873510_704318' ,
2520         'tag_200_code_e_873510_280822' ,
2521         'tag_200_subfield_e_873510_280822' ,
2522         'tag_200_code_f_873510_110730' ,
2523         'tag_200_subfield_f_873510_110730' ,
2524     }
2525     L<$record> is the MARC::Record object.
2526
2527 =cut
2528
2529 sub TransformHtmlToMarc {
2530     my ($cgi, $isbiblio) = @_;
2531
2532     my @params = $cgi->multi_param();
2533
2534     # explicitly turn on the UTF-8 flag for all
2535     # 'tag_' parameters to avoid incorrect character
2536     # conversion later on
2537     my $cgi_params = $cgi->Vars;
2538     foreach my $param_name ( keys %$cgi_params ) {
2539         if ( $param_name =~ /^tag_/ ) {
2540             my $param_value = $cgi_params->{$param_name};
2541             unless ( Encode::is_utf8( $param_value ) ) {
2542                 $cgi_params->{$param_name} = Encode::decode('UTF-8', $param_value );
2543             }
2544         }
2545     }
2546
2547     # creating a new record
2548     my $record = MARC::Record->new();
2549     my @fields;
2550     my ($biblionumbertagfield, $biblionumbertagsubfield) = (-1, -1);
2551     ($biblionumbertagfield, $biblionumbertagsubfield) =
2552         &GetMarcFromKohaField( "biblio.biblionumber", '' ) if $isbiblio;
2553 #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!
2554     for (my $i = 0; $params[$i]; $i++ ) {    # browse all CGI params
2555         my $param    = $params[$i];
2556         my $newfield = 0;
2557
2558         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
2559         if ( $param eq 'biblionumber' ) {
2560             if ( $biblionumbertagfield < 10 ) {
2561                 $newfield = MARC::Field->new( $biblionumbertagfield, scalar $cgi->param($param), );
2562             } else {
2563                 $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => scalar $cgi->param($param), );
2564             }
2565             push @fields, $newfield if ($newfield);
2566         } elsif ( $param =~ /^tag_(\d*)_indicator1_/ ) {    # new field start when having 'input name="..._indicator1_..."
2567             my $tag = $1;
2568
2569             my $ind1 = _default_ind_to_space( substr( $cgi->param($param), 0, 1 ) );
2570             my $ind2 = _default_ind_to_space( substr( $cgi->param( $params[ $i + 1 ] ), 0, 1 ) );
2571             $newfield = 0;
2572             my $j = $i + 2;
2573
2574             if ( $tag < 10 ) {                              # no code for theses fields
2575                                                             # in MARC editor, 000 contains the leader.
2576                 next if $tag == $biblionumbertagfield;
2577                 my $fval= $cgi->param($params[$j+1]);
2578                 if ( $tag eq '000' ) {
2579                     # Force a fake leader even if not provided to avoid crashing
2580                     # during decoding MARC record containing UTF-8 characters
2581                     $record->leader(
2582                         length( $fval ) == 24
2583                         ? $fval
2584                         : '     nam a22        4500'
2585                         )
2586                     ;
2587                     # between 001 and 009 (included)
2588                 } elsif ( $fval ne '' ) {
2589                     $newfield = MARC::Field->new( $tag, $fval, );
2590                 }
2591
2592                 # > 009, deal with subfields
2593             } else {
2594                 # browse subfields for this tag (reason for _code_ match)
2595                 while(defined $params[$j] && $params[$j] =~ /_code_/) {
2596                     last unless defined $params[$j+1];
2597                     $j += 2 and next
2598                         if $tag == $biblionumbertagfield and
2599                            $cgi->param($params[$j]) eq $biblionumbertagsubfield;
2600                     #if next param ne subfield, then it was probably empty
2601                     #try next param by incrementing j
2602                     if($params[$j+1]!~/_subfield_/) {$j++; next; }
2603                     my $fkey= $cgi->param($params[$j]);
2604                     my $fval= $cgi->param($params[$j+1]);
2605                     #check if subfield value not empty and field exists
2606                     if($fval ne '' && $newfield) {
2607                         $newfield->add_subfields( $fkey => $fval);
2608                     }
2609                     elsif($fval ne '') {
2610                         $newfield = MARC::Field->new( $tag, $ind1, $ind2, $fkey => $fval );
2611                     }
2612                     $j += 2;
2613                 } #end-of-while
2614                 $i= $j-1; #update i for outer loop accordingly
2615             }
2616             push @fields, $newfield if ($newfield);
2617         }
2618     }
2619
2620     $record->append_fields(@fields);
2621     return $record;
2622 }
2623
2624 =head2 TransformMarcToKoha
2625
2626   $result = TransformMarcToKoha( $record, $frameworkcode )
2627
2628 Extract data from a MARC bib record into a hashref representing
2629 Koha biblio, biblioitems, and items fields. 
2630
2631 If passed an undefined record will log the error and return an empty
2632 hash_ref
2633
2634 =cut
2635
2636 sub TransformMarcToKoha {
2637     my ( $record, $frameworkcode, $limit_table ) = @_;
2638
2639     my $result = {};
2640     if (!defined $record) {
2641         carp('TransformMarcToKoha called with undefined record');
2642         return $result;
2643     }
2644     $limit_table = $limit_table || 0;
2645     $frameworkcode = '' unless defined $frameworkcode;
2646
2647     my $inverted_field_map = _get_inverted_marc_field_map($frameworkcode);
2648
2649     my %tables = ();
2650     if ( defined $limit_table && $limit_table eq 'items' ) {
2651         $tables{'items'} = 1;
2652     } else {
2653         $tables{'items'}       = 1;
2654         $tables{'biblio'}      = 1;
2655         $tables{'biblioitems'} = 1;
2656     }
2657
2658     # traverse through record
2659   MARCFIELD: foreach my $field ( $record->fields() ) {
2660         my $tag = $field->tag();
2661         next MARCFIELD unless exists $inverted_field_map->{$tag};
2662         if ( $field->is_control_field() ) {
2663             my $kohafields = $inverted_field_map->{$tag}->{list};
2664           ENTRY: foreach my $entry ( @{$kohafields} ) {
2665                 my ( $subfield, $table, $column ) = @{$entry};
2666                 next ENTRY unless exists $tables{$table};
2667                 my $key = _disambiguate( $table, $column );
2668                 if ( $result->{$key} ) {
2669                     unless ( ( $key eq "biblionumber" or $key eq "biblioitemnumber" ) and ( $field->data() eq "" ) ) {
2670                         $result->{$key} .= " | " . $field->data();
2671                     }
2672                 } else {
2673                     $result->{$key} = $field->data();
2674                 }
2675             }
2676         } else {
2677
2678             # deal with subfields
2679           MARCSUBFIELD: foreach my $sf ( $field->subfields() ) {
2680                 my $code = $sf->[0];
2681                 next MARCSUBFIELD unless exists $inverted_field_map->{$tag}->{sfs}->{$code};
2682                 my $value = $sf->[1];
2683               SFENTRY: foreach my $entry ( @{ $inverted_field_map->{$tag}->{sfs}->{$code} } ) {
2684                     my ( $table, $column ) = @{$entry};
2685                     next SFENTRY unless exists $tables{$table};
2686                     my $key = _disambiguate( $table, $column );
2687                     if ( $result->{$key} ) {
2688                         unless ( ( $key eq "biblionumber" or $key eq "biblioitemnumber" ) and ( $value eq "" ) ) {
2689                             $result->{$key} .= " | " . $value;
2690                         }
2691                     } else {
2692                         $result->{$key} = $value;
2693                     }
2694                 }
2695             }
2696         }
2697     }
2698
2699     # modify copyrightdate to keep only the 1st year found
2700     if ( exists $result->{'copyrightdate'} ) {
2701         my $temp = $result->{'copyrightdate'};
2702         $temp =~ m/c(\d\d\d\d)/;
2703         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) {    # search cYYYY first
2704             $result->{'copyrightdate'} = $1;
2705         } else {                                       # if no cYYYY, get the 1st date.
2706             $temp =~ m/(\d\d\d\d)/;
2707             $result->{'copyrightdate'} = $1;
2708         }
2709     }
2710
2711     # modify publicationyear to keep only the 1st year found
2712     if ( exists $result->{'publicationyear'} ) {
2713         my $temp = $result->{'publicationyear'};
2714         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) {    # search cYYYY first
2715             $result->{'publicationyear'} = $1;
2716         } else {                                       # if no cYYYY, get the 1st date.
2717             $temp =~ m/(\d\d\d\d)/;
2718             $result->{'publicationyear'} = $1;
2719         }
2720     }
2721
2722     return $result;
2723 }
2724
2725 sub _get_inverted_marc_field_map {
2726     my ( $frameworkcode ) = @_;
2727     my $field_map = {};
2728     my $mss = GetMarcSubfieldStructure( $frameworkcode );
2729
2730     foreach my $kohafield ( keys %{ $mss } ) {
2731         next unless exists $mss->{$kohafield};    # not all columns are mapped to MARC tag & subfield
2732         my $tag      = $mss->{$kohafield}{tagfield};
2733         my $subfield = $mss->{$kohafield}{tagsubfield};
2734         my ( $table, $column ) = split /[.]/, $kohafield, 2;
2735         push @{ $field_map->{$tag}->{list} }, [ $subfield, $table, $column ];
2736         push @{ $field_map->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
2737     }
2738     return $field_map;
2739 }
2740
2741 =head2 _disambiguate
2742
2743   $newkey = _disambiguate($table, $field);
2744
2745 This is a temporary hack to distinguish between the
2746 following sets of columns when using TransformMarcToKoha.
2747
2748   items.cn_source & biblioitems.cn_source
2749   items.cn_sort & biblioitems.cn_sort
2750
2751 Columns that are currently NOT distinguished (FIXME
2752 due to lack of time to fully test) are:
2753
2754   biblio.notes and biblioitems.notes
2755   biblionumber
2756   timestamp
2757   biblioitemnumber
2758
2759 FIXME - this is necessary because prefixing each column
2760 name with the table name would require changing lots
2761 of code and templates, and exposing more of the DB
2762 structure than is good to the UI templates, particularly
2763 since biblio and bibloitems may well merge in a future
2764 version.  In the future, it would also be good to 
2765 separate DB access and UI presentation field names
2766 more.
2767
2768 =cut
2769
2770 sub CountItemsIssued {
2771     my ($biblionumber) = @_;
2772     my $dbh            = C4::Context->dbh;
2773     my $sth            = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
2774     $sth->execute($biblionumber);
2775     my $row = $sth->fetchrow_hashref();
2776     return $row->{'issuedCount'};
2777 }
2778
2779 sub _disambiguate {
2780     my ( $table, $column ) = @_;
2781     if ( $column eq "cn_sort" or $column eq "cn_source" ) {
2782         return $table . '.' . $column;
2783     } else {
2784         return $column;
2785     }
2786
2787 }
2788
2789 =head2 get_koha_field_from_marc
2790
2791   $result->{_disambiguate($table, $field)} = 
2792      get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2793
2794 Internal function to map data from the MARC record to a specific non-MARC field.
2795 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2796
2797 =cut
2798
2799 sub get_koha_field_from_marc {
2800     my ( $koha_table, $koha_column, $record, $frameworkcode ) = @_;
2801     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table . '.' . $koha_column, $frameworkcode );
2802     my $kohafield;
2803     foreach my $field ( $record->field($tagfield) ) {
2804         if ( $field->tag() < 10 ) {
2805             if ($kohafield) {
2806                 $kohafield .= " | " . $field->data();
2807             } else {
2808                 $kohafield = $field->data();
2809             }
2810         } else {
2811             if ( $field->subfields ) {
2812                 my @subfields = $field->subfields();
2813                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2814                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2815                         if ($kohafield) {
2816                             $kohafield .= " | " . $subfields[$subfieldcount][1];
2817                         } else {
2818                             $kohafield = $subfields[$subfieldcount][1];
2819                         }
2820                     }
2821                 }
2822             }
2823         }
2824     }
2825     return $kohafield;
2826 }
2827
2828 =head2 TransformMarcToKohaOneField
2829
2830   $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2831
2832 =cut
2833
2834 sub TransformMarcToKohaOneField {
2835
2836     # FIXME ? if a field has a repeatable subfield that is used in old-db,
2837     # only the 1st will be retrieved...
2838     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2839     my $res = "";
2840     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $kohatable . "." . $kohafield, $frameworkcode );
2841     foreach my $field ( $record->field($tagfield) ) {
2842         if ( $field->tag() < 10 ) {
2843             if ( $result->{$kohafield} ) {
2844                 $result->{$kohafield} .= " | " . $field->data();
2845             } else {
2846                 $result->{$kohafield} = $field->data();
2847             }
2848         } else {
2849             if ( $field->subfields ) {
2850                 my @subfields = $field->subfields();
2851                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2852                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2853                         if ( $result->{$kohafield} ) {
2854                             $result->{$kohafield} .= " | " . $subfields[$subfieldcount][1];
2855                         } else {
2856                             $result->{$kohafield} = $subfields[$subfieldcount][1];
2857                         }
2858                     }
2859                 }
2860             }
2861         }
2862     }
2863     return $result;
2864 }
2865
2866
2867 #"
2868
2869 #
2870 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2871 # at the same time
2872 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2873 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2874 # =head2 ModZebrafiles
2875 #
2876 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2877 #
2878 # =cut
2879 #
2880 # sub ModZebrafiles {
2881 #
2882 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2883 #
2884 #     my $op;
2885 #     my $zebradir =
2886 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2887 #     unless ( opendir( DIR, "$zebradir" ) ) {
2888 #         warn "$zebradir not found";
2889 #         return;
2890 #     }
2891 #     closedir DIR;
2892 #     my $filename = $zebradir . $biblionumber;
2893 #
2894 #     if ($record) {
2895 #         open( OUTPUT, ">", $filename . ".xml" );
2896 #         print OUTPUT $record;
2897 #         close OUTPUT;
2898 #     }
2899 # }
2900
2901 =head2 ModZebra
2902
2903   ModZebra( $biblionumber, $op, $server, $record );
2904
2905 $biblionumber is the biblionumber we want to index
2906
2907 $op is specialUpdate or recordDelete, and is used to know what we want to do
2908
2909 $server is the server that we want to update
2910
2911 $record is the update MARC record if it's available. If it's not supplied
2912 and is needed, it'll be loaded from the database.
2913
2914 =cut
2915
2916 sub ModZebra {
2917 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2918     my ( $biblionumber, $op, $server, $record ) = @_;
2919     $debug && warn "ModZebra: update requested for: $biblionumber $op $server\n";
2920     if ( C4::Context->preference('SearchEngine') eq 'Elasticsearch' ) {
2921
2922         # TODO abstract to a standard API that'll work for whatever
2923         require Koha::ElasticSearch::Indexer;
2924         my $indexer = Koha::ElasticSearch::Indexer->new(
2925             {
2926                 index => $server eq 'biblioserver'
2927                 ? $Koha::SearchEngine::BIBLIOS_INDEX
2928                 : $Koha::SearchEngine::AUTHORITIES_INDEX
2929             }
2930         );
2931         if ( $op eq 'specialUpdate' ) {
2932             unless ($record) {
2933                 $record = GetMarcBiblio($biblionumber, 1);
2934             }
2935             my $records = [$record];
2936             $indexer->update_index_background( [$biblionumber], [$record] );
2937         }
2938         elsif ( $op eq 'recordDelete' ) {
2939             $indexer->delete_index_background( [$biblionumber] );
2940         }
2941         else {
2942             croak "ModZebra called with unknown operation: $op";
2943         }
2944     }
2945
2946     my $dbh = C4::Context->dbh;
2947
2948     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2949     # at the same time
2950     # replaced by a zebraqueue table, that is filled with ModZebra to run.
2951     # the table is emptied by rebuild_zebra.pl script (using the -z switch)
2952     my $check_sql = "SELECT COUNT(*) FROM zebraqueue
2953     WHERE server = ?
2954         AND   biblio_auth_number = ?
2955         AND   operation = ?
2956         AND   done = 0";
2957     my $check_sth = $dbh->prepare_cached($check_sql);
2958     $check_sth->execute( $server, $biblionumber, $op );
2959     my ($count) = $check_sth->fetchrow_array;
2960     $check_sth->finish();
2961     if ( $count == 0 ) {
2962         my $sth = $dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2963         $sth->execute( $biblionumber, $server, $op );
2964         $sth->finish;
2965     }
2966 }
2967
2968
2969 =head2 EmbedItemsInMarcBiblio
2970
2971     EmbedItemsInMarcBiblio($marc, $biblionumber, $itemnumbers, $opac);
2972
2973 Given a MARC::Record object containing a bib record,
2974 modify it to include the items attached to it as 9XX
2975 per the bib's MARC framework.
2976 if $itemnumbers is defined, only specified itemnumbers are embedded.
2977
2978 If $opac is true, then opac-relevant suppressions are included.
2979
2980 =cut
2981
2982 sub EmbedItemsInMarcBiblio {
2983     my ($marc, $biblionumber, $itemnumbers, $opac) = @_;
2984     if ( !$marc ) {
2985         carp 'EmbedItemsInMarcBiblio: No MARC record passed';
2986         return;
2987     }
2988
2989     $itemnumbers = [] unless defined $itemnumbers;
2990
2991     my $frameworkcode = GetFrameworkCode($biblionumber);
2992     _strip_item_fields($marc, $frameworkcode);
2993
2994     # ... and embed the current items
2995     my $dbh = C4::Context->dbh;
2996     my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
2997     $sth->execute($biblionumber);
2998     my @item_fields;
2999     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
3000     my @items;
3001     my $opachiddenitems = $opac
3002       && ( C4::Context->preference('OpacHiddenItems') !~ /^\s*$/ );
3003     require C4::Items;
3004     while ( my ($itemnumber) = $sth->fetchrow_array ) {
3005         next if @$itemnumbers and not grep { $_ == $itemnumber } @$itemnumbers;
3006         my $i = $opachiddenitems ? C4::Items::GetItem($itemnumber) : undef;
3007         push @items, { itemnumber => $itemnumber, item => $i };
3008     }
3009     my @hiddenitems =
3010       $opachiddenitems
3011       ? C4::Items::GetHiddenItemnumbers( map { $_->{item} } @items )
3012       : ();
3013     # Convert to a hash for quick searching
3014     my %hiddenitems = map { $_ => 1 } @hiddenitems;
3015     foreach my $itemnumber ( map { $_->{itemnumber} } @items ) {
3016         next if $hiddenitems{$itemnumber};
3017         my $item_marc = C4::Items::GetMarcItem( $biblionumber, $itemnumber );
3018         push @item_fields, $item_marc->field($itemtag);
3019     }
3020     $marc->append_fields(@item_fields);
3021 }
3022
3023 =head1 INTERNAL FUNCTIONS
3024
3025 =head2 _koha_marc_update_bib_ids
3026
3027
3028   _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
3029
3030 Internal function to add or update biblionumber and biblioitemnumber to
3031 the MARC XML.
3032
3033 =cut
3034
3035 sub _koha_marc_update_bib_ids {
3036     my ( $record, $frameworkcode, $biblionumber, $biblioitemnumber ) = @_;
3037
3038     # we must add bibnum and bibitemnum in MARC::Record...
3039     # we build the new field with biblionumber and biblioitemnumber
3040     # we drop the original field
3041     # we add the new builded field.
3042     my ( $biblio_tag,     $biblio_subfield )     = GetMarcFromKohaField( "biblio.biblionumber",          $frameworkcode );
3043     die qq{No biblionumber tag for framework "$frameworkcode"} unless $biblio_tag;
3044     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber", $frameworkcode );
3045     die qq{No biblioitemnumber tag for framework "$frameworkcode"} unless $biblioitem_tag;
3046
3047     if ( $biblio_tag == $biblioitem_tag ) {
3048
3049         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
3050         my $new_field = MARC::Field->new(
3051             $biblio_tag, '', '',
3052             "$biblio_subfield"     => $biblionumber,
3053             "$biblioitem_subfield" => $biblioitemnumber
3054         );
3055
3056         # drop old field and create new one...
3057         my $old_field = $record->field($biblio_tag);
3058         $record->delete_field($old_field) if $old_field;
3059         $record->insert_fields_ordered($new_field);
3060     } else {
3061
3062         # biblionumber & biblioitemnumber are in different fields
3063
3064         # deal with biblionumber
3065         my ( $new_field, $old_field );
3066         if ( $biblio_tag < 10 ) {
3067             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
3068         } else {
3069             $new_field = MARC::Field->new( $biblio_tag, '', '', "$biblio_subfield" => $biblionumber );
3070         }
3071
3072         # drop old field and create new one...
3073         $old_field = $record->field($biblio_tag);
3074         $record->delete_field($old_field) if $old_field;
3075         $record->insert_fields_ordered($new_field);
3076
3077         # deal with biblioitemnumber
3078         if ( $biblioitem_tag < 10 ) {
3079             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
3080         } else {
3081             $new_field = MARC::Field->new( $biblioitem_tag, '', '', "$biblioitem_subfield" => $biblioitemnumber, );
3082         }
3083
3084         # drop old field and create new one...
3085         $old_field = $record->field($biblioitem_tag);
3086         $record->delete_field($old_field) if $old_field;
3087         $record->insert_fields_ordered($new_field);
3088     }
3089 }
3090
3091 =head2 _koha_marc_update_biblioitem_cn_sort
3092
3093   _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
3094
3095 Given a MARC bib record and the biblioitem hash, update the
3096 subfield that contains a copy of the value of biblioitems.cn_sort.
3097
3098 =cut
3099
3100 sub _koha_marc_update_biblioitem_cn_sort {
3101     my $marc          = shift;
3102     my $biblioitem    = shift;
3103     my $frameworkcode = shift;
3104
3105     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.cn_sort", $frameworkcode );
3106     return unless $biblioitem_tag;
3107
3108     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3109
3110     if ( my $field = $marc->field($biblioitem_tag) ) {
3111         $field->delete_subfield( code => $biblioitem_subfield );
3112         if ( $cn_sort ne '' ) {
3113             $field->add_subfields( $biblioitem_subfield => $cn_sort );
3114         }
3115     } else {
3116
3117         # if we get here, no biblioitem tag is present in the MARC record, so
3118         # we'll create it if $cn_sort is not empty -- this would be
3119         # an odd combination of events, however
3120         if ($cn_sort) {
3121             $marc->insert_grouped_field( MARC::Field->new( $biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort ) );
3122         }
3123     }
3124 }
3125
3126 =head2 _koha_add_biblio
3127
3128   my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
3129
3130 Internal function to add a biblio ($biblio is a hash with the values)
3131
3132 =cut
3133
3134 sub _koha_add_biblio {
3135     my ( $dbh, $biblio, $frameworkcode ) = @_;
3136
3137     my $error;
3138
3139     # set the series flag
3140     unless (defined $biblio->{'serial'}){
3141         $biblio->{'serial'} = 0;
3142         if ( $biblio->{'seriestitle'} ) { $biblio->{'serial'} = 1 }
3143     }
3144
3145     my $query = "INSERT INTO biblio
3146         SET frameworkcode = ?,
3147             author = ?,
3148             title = ?,
3149             unititle =?,
3150             notes = ?,
3151             serial = ?,
3152             seriestitle = ?,
3153             copyrightdate = ?,
3154             datecreated=NOW(),
3155             abstract = ?
3156         ";
3157     my $sth = $dbh->prepare($query);
3158     $sth->execute(
3159         $frameworkcode, $biblio->{'author'},      $biblio->{'title'},         $biblio->{'unititle'}, $biblio->{'notes'},
3160         $biblio->{'serial'},        $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}
3161     );
3162
3163     my $biblionumber = $dbh->{'mysql_insertid'};
3164     if ( $dbh->errstr ) {
3165         $error .= "ERROR in _koha_add_biblio $query" . $dbh->errstr;
3166         warn $error;
3167     }
3168
3169     $sth->finish();
3170
3171     #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3172     return ( $biblionumber, $error );
3173 }
3174
3175 =head2 _koha_modify_biblio
3176
3177   my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3178
3179 Internal function for updating the biblio table
3180
3181 =cut
3182
3183 sub _koha_modify_biblio {
3184     my ( $dbh, $biblio, $frameworkcode ) = @_;
3185     my $error;
3186
3187     my $query = "
3188         UPDATE biblio
3189         SET    frameworkcode = ?,
3190                author = ?,
3191                title = ?,
3192                unititle = ?,
3193                notes = ?,
3194                serial = ?,
3195                seriestitle = ?,
3196                copyrightdate = ?,
3197                abstract = ?
3198         WHERE  biblionumber = ?
3199         "
3200       ;
3201     my $sth = $dbh->prepare($query);
3202
3203     $sth->execute(
3204         $frameworkcode,      $biblio->{'author'},      $biblio->{'title'},         $biblio->{'unititle'}, $biblio->{'notes'},
3205         $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}, $biblio->{'biblionumber'}
3206     ) if $biblio->{'biblionumber'};
3207
3208     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3209         $error .= "ERROR in _koha_modify_biblio $query" . $dbh->errstr;
3210         warn $error;
3211     }
3212     return ( $biblio->{'biblionumber'}, $error );
3213 }
3214
3215 =head2 _koha_modify_biblioitem_nonmarc
3216
3217   my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3218
3219 Updates biblioitems row except for marc and marcxml, which should be changed
3220 via ModBiblioMarc
3221
3222 =cut
3223
3224 sub _koha_modify_biblioitem_nonmarc {
3225     my ( $dbh, $biblioitem ) = @_;
3226     my $error;
3227
3228     # re-calculate the cn_sort, it may have changed
3229     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3230
3231     my $query = "UPDATE biblioitems 
3232     SET biblionumber    = ?,
3233         volume          = ?,
3234         number          = ?,
3235         itemtype        = ?,
3236         isbn            = ?,
3237         issn            = ?,
3238         publicationyear = ?,
3239         publishercode   = ?,
3240         volumedate      = ?,
3241         volumedesc      = ?,
3242         collectiontitle = ?,
3243         collectionissn  = ?,
3244         collectionvolume= ?,
3245         editionstatement= ?,
3246         editionresponsibility = ?,
3247         illus           = ?,
3248         pages           = ?,
3249         notes           = ?,
3250         size            = ?,
3251         place           = ?,
3252         lccn            = ?,
3253         url             = ?,
3254         cn_source       = ?,
3255         cn_class        = ?,
3256         cn_item         = ?,
3257         cn_suffix       = ?,
3258         cn_sort         = ?,
3259         totalissues     = ?,
3260         ean             = ?,
3261         agerestriction  = ?
3262         where biblioitemnumber = ?
3263         ";
3264     my $sth = $dbh->prepare($query);
3265     $sth->execute(
3266         $biblioitem->{'biblionumber'},     $biblioitem->{'volume'},           $biblioitem->{'number'},                $biblioitem->{'itemtype'},
3267         $biblioitem->{'isbn'},             $biblioitem->{'issn'},             $biblioitem->{'publicationyear'},       $biblioitem->{'publishercode'},
3268         $biblioitem->{'volumedate'},       $biblioitem->{'volumedesc'},       $biblioitem->{'collectiontitle'},       $biblioitem->{'collectionissn'},
3269         $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
3270         $biblioitem->{'pages'},            $biblioitem->{'bnotes'},           $biblioitem->{'size'},                  $biblioitem->{'place'},
3271         $biblioitem->{'lccn'},             $biblioitem->{'url'},              $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'},
3272         $biblioitem->{'cn_item'},          $biblioitem->{'cn_suffix'},        $cn_sort,                               $biblioitem->{'totalissues'},
3273         $biblioitem->{'ean'},              $biblioitem->{'agerestriction'},   $biblioitem->{'biblioitemnumber'}
3274     );
3275     if ( $dbh->errstr ) {
3276         $error .= "ERROR in _koha_modify_biblioitem_nonmarc $query" . $dbh->errstr;
3277         warn $error;
3278     }
3279     return ( $biblioitem->{'biblioitemnumber'}, $error );
3280 }
3281
3282 =head2 _koha_add_biblioitem
3283
3284   my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3285
3286 Internal function to add a biblioitem
3287
3288 =cut
3289
3290 sub _koha_add_biblioitem {
3291     my ( $dbh, $biblioitem ) = @_;
3292     my $error;
3293
3294     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3295     my $query = "INSERT INTO biblioitems SET
3296         biblionumber    = ?,
3297         volume          = ?,
3298         number          = ?,
3299         itemtype        = ?,
3300         isbn            = ?,
3301         issn            = ?,
3302         publicationyear = ?,
3303         publishercode   = ?,
3304         volumedate      = ?,
3305         volumedesc      = ?,
3306         collectiontitle = ?,
3307         collectionissn  = ?,
3308         collectionvolume= ?,
3309         editionstatement= ?,
3310         editionresponsibility = ?,
3311         illus           = ?,
3312         pages           = ?,
3313         notes           = ?,
3314         size            = ?,
3315         place           = ?,
3316         lccn            = ?,
3317         marc            = ?,
3318         url             = ?,
3319         cn_source       = ?,
3320         cn_class        = ?,
3321         cn_item         = ?,
3322         cn_suffix       = ?,
3323         cn_sort         = ?,
3324         totalissues     = ?,
3325         ean             = ?,
3326         agerestriction  = ?
3327         ";
3328     my $sth = $dbh->prepare($query);
3329     $sth->execute(
3330         $biblioitem->{'biblionumber'},     $biblioitem->{'volume'},           $biblioitem->{'number'},                $biblioitem->{'itemtype'},
3331         $biblioitem->{'isbn'},             $biblioitem->{'issn'},             $biblioitem->{'publicationyear'},       $biblioitem->{'publishercode'},
3332         $biblioitem->{'volumedate'},       $biblioitem->{'volumedesc'},       $biblioitem->{'collectiontitle'},       $biblioitem->{'collectionissn'},
3333         $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
3334         $biblioitem->{'pages'},            $biblioitem->{'bnotes'},           $biblioitem->{'size'},                  $biblioitem->{'place'},
3335         $biblioitem->{'lccn'},             $biblioitem->{'marc'},             $biblioitem->{'url'},                   $biblioitem->{'biblioitems.cn_source'},
3336         $biblioitem->{'cn_class'},         $biblioitem->{'cn_item'},          $biblioitem->{'cn_suffix'},             $cn_sort,
3337         $biblioitem->{'totalissues'},      $biblioitem->{'ean'},              $biblioitem->{'agerestriction'}
3338     );
3339     my $bibitemnum = $dbh->{'mysql_insertid'};
3340
3341     if ( $dbh->errstr ) {
3342         $error .= "ERROR in _koha_add_biblioitem $query" . $dbh->errstr;
3343         warn $error;
3344     }
3345     $sth->finish();
3346     return ( $bibitemnum, $error );
3347 }
3348
3349 =head2 _koha_delete_biblio
3350
3351   $error = _koha_delete_biblio($dbh,$biblionumber);
3352
3353 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3354
3355 C<$dbh> - the database handle
3356
3357 C<$biblionumber> - the biblionumber of the biblio to be deleted
3358
3359 =cut
3360
3361 # FIXME: add error handling
3362
3363 sub _koha_delete_biblio {
3364     my ( $dbh, $biblionumber ) = @_;
3365
3366     # get all the data for this biblio
3367     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3368     $sth->execute($biblionumber);
3369
3370     if ( my $data = $sth->fetchrow_hashref ) {
3371
3372         # save the record in deletedbiblio
3373         # find the fields to save
3374         my $query = "INSERT INTO deletedbiblio SET ";
3375         my @bind  = ();
3376         foreach my $temp ( keys %$data ) {
3377             $query .= "$temp = ?,";
3378             push( @bind, $data->{$temp} );
3379         }
3380
3381         # replace the last , by ",?)"
3382         $query =~ s/\,$//;
3383         my $bkup_sth = $dbh->prepare($query);
3384         $bkup_sth->execute(@bind);
3385         $bkup_sth->finish;
3386
3387         # delete the biblio
3388         my $sth2 = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3389         $sth2->execute($biblionumber);
3390         # update the timestamp (Bugzilla 7146)
3391         $sth2= $dbh->prepare("UPDATE deletedbiblio SET timestamp=NOW() WHERE biblionumber=?");
3392         $sth2->execute($biblionumber);
3393         $sth2->finish;
3394     }
3395     $sth->finish;
3396     return;
3397 }
3398
3399 =head2 _koha_delete_biblioitems
3400
3401   $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3402
3403 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3404
3405 C<$dbh> - the database handle
3406 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3407
3408 =cut
3409
3410 # FIXME: add error handling
3411
3412 sub _koha_delete_biblioitems {
3413     my ( $dbh, $biblioitemnumber ) = @_;
3414
3415     # get all the data for this biblioitem
3416     my $sth = $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3417     $sth->execute($biblioitemnumber);
3418
3419     if ( my $data = $sth->fetchrow_hashref ) {
3420
3421         # save the record in deletedbiblioitems
3422         # find the fields to save
3423         my $query = "INSERT INTO deletedbiblioitems SET ";
3424         my @bind  = ();
3425         foreach my $temp ( keys %$data ) {
3426             $query .= "$temp = ?,";
3427             push( @bind, $data->{$temp} );
3428         }
3429
3430         # replace the last , by ",?)"
3431         $query =~ s/\,$//;
3432         my $bkup_sth = $dbh->prepare($query);
3433         $bkup_sth->execute(@bind);
3434         $bkup_sth->finish;
3435
3436         # delete the biblioitem
3437         my $sth2 = $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3438         $sth2->execute($biblioitemnumber);
3439         # update the timestamp (Bugzilla 7146)
3440         $sth2= $dbh->prepare("UPDATE deletedbiblioitems SET timestamp=NOW() WHERE biblioitemnumber=?");
3441         $sth2->execute($biblioitemnumber);
3442         $sth2->finish;
3443     }
3444     $sth->finish;
3445     return;
3446 }
3447
3448 =head1 UNEXPORTED FUNCTIONS
3449
3450 =head2 ModBiblioMarc
3451
3452   &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3453
3454 Add MARC data for a biblio to koha 
3455
3456 Function exported, but should NOT be used, unless you really know what you're doing
3457
3458 =cut
3459
3460 sub ModBiblioMarc {
3461     # pass the MARC::Record to this function, and it will create the records in
3462     # the marc field
3463     my ( $record, $biblionumber, $frameworkcode ) = @_;
3464     if ( !$record ) {
3465         carp 'ModBiblioMarc passed an undefined record';
3466         return;
3467     }
3468
3469     # Clone record as it gets modified
3470     $record = $record->clone();
3471     my $dbh    = C4::Context->dbh;
3472     my @fields = $record->fields();
3473     if ( !$frameworkcode ) {
3474         $frameworkcode = "";
3475     }
3476     my $sth = $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3477     $sth->execute( $frameworkcode, $biblionumber );
3478     $sth->finish;
3479     my $encoding = C4::Context->preference("marcflavour");
3480
3481     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3482     if ( $encoding eq "UNIMARC" ) {
3483         my $defaultlanguage = C4::Context->preference("UNIMARCField100Language");
3484         $defaultlanguage = "fre" if (!$defaultlanguage || length($defaultlanguage) != 3);
3485         my $string = $record->subfield( 100, "a" );
3486         if ( ($string) && ( length( $record->subfield( 100, "a" ) ) == 36 ) ) {
3487             my $f100 = $record->field(100);
3488             $record->delete_field($f100);
3489         } else {
3490             $string = POSIX::strftime( "%Y%m%d", localtime );
3491             $string =~ s/\-//g;
3492             $string = sprintf( "%-*s", 35, $string );
3493             substr ( $string, 22, 3, $defaultlanguage);
3494         }
3495         substr( $string, 25, 3, "y50" );
3496         unless ( $record->subfield( 100, "a" ) ) {
3497             $record->insert_fields_ordered( MARC::Field->new( 100, "", "", "a" => $string ) );
3498         }
3499     }
3500
3501     #enhancement 5374: update transaction date (005) for marc21/unimarc
3502     if($encoding =~ /MARC21|UNIMARC/) {
3503       my @a= (localtime) [5,4,3,2,1,0]; $a[0]+=1900; $a[1]++;
3504         # YY MM DD HH MM SS (update year and month)
3505       my $f005= $record->field('005');
3506       $f005->update(sprintf("%4d%02d%02d%02d%02d%04.1f",@a)) if $f005;
3507     }
3508
3509     $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
3510     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding), $biblionumber );
3511     $sth->finish;
3512     ModZebra( $biblionumber, "specialUpdate", "biblioserver", $record );
3513     return $biblionumber;
3514 }
3515
3516 =head2 CountBiblioInOrders
3517
3518     $count = &CountBiblioInOrders( $biblionumber);
3519
3520 This function return count of biblios in orders with $biblionumber 
3521
3522 =cut
3523
3524 sub CountBiblioInOrders {
3525  my ($biblionumber) = @_;
3526     my $dbh            = C4::Context->dbh;
3527     my $query          = "SELECT count(*)
3528           FROM  aqorders 
3529           WHERE biblionumber=? AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')";
3530     my $sth = $dbh->prepare($query);
3531     $sth->execute($biblionumber);
3532     my $count = $sth->fetchrow;
3533     return ($count);
3534 }
3535
3536 =head2 GetSubscriptionsId
3537
3538     $subscriptions = &GetSubscriptionsId($biblionumber);
3539
3540 This function return an array of subscriptionid with $biblionumber
3541
3542 =cut
3543
3544 sub GetSubscriptionsId {
3545  my ($biblionumber) = @_;
3546     my $dbh            = C4::Context->dbh;
3547     my $query          = "SELECT subscriptionid
3548           FROM  subscription
3549           WHERE biblionumber=?";
3550     my $sth = $dbh->prepare($query);
3551     $sth->execute($biblionumber);
3552     my @subscriptions = $sth->fetchrow_array;
3553     return (@subscriptions);
3554 }
3555
3556 =head2 GetHolds
3557
3558     $holds = &GetHolds($biblionumber);
3559
3560 This function return the count of holds with $biblionumber
3561
3562 =cut
3563
3564 sub GetHolds {
3565  my ($biblionumber) = @_;
3566     my $dbh            = C4::Context->dbh;
3567     my $query          = "SELECT count(*)
3568           FROM  reserves
3569           WHERE biblionumber=?";
3570     my $sth = $dbh->prepare($query);
3571     $sth->execute($biblionumber);
3572     my $holds = $sth->fetchrow;
3573     return ($holds);
3574 }
3575
3576 =head2 prepare_host_field
3577
3578 $marcfield = prepare_host_field( $hostbiblioitem, $marcflavour );
3579 Generate the host item entry for an analytic child entry
3580
3581 =cut
3582
3583 sub prepare_host_field {
3584     my ( $hostbiblio, $marcflavour ) = @_;
3585     $marcflavour ||= C4::Context->preference('marcflavour');
3586     my $host = GetMarcBiblio($hostbiblio);
3587     # unfortunately as_string does not 'do the right thing'
3588     # if field returns undef
3589     my %sfd;
3590     my $field;
3591     my $host_field;
3592     if ( $marcflavour eq 'MARC21' || $marcflavour eq 'NORMARC' ) {
3593         if ( $field = $host->field('100') || $host->field('110') || $host->field('11') ) {
3594             my $s = $field->as_string('ab');
3595             if ($s) {
3596                 $sfd{a} = $s;
3597             }
3598         }
3599         if ( $field = $host->field('245') ) {
3600             my $s = $field->as_string('a');
3601             if ($s) {
3602                 $sfd{t} = $s;
3603             }
3604         }
3605         if ( $field = $host->field('260') ) {
3606             my $s = $field->as_string('abc');
3607             if ($s) {
3608                 $sfd{d} = $s;
3609             }
3610         }
3611         if ( $field = $host->field('240') ) {
3612             my $s = $field->as_string();
3613             if ($s) {
3614                 $sfd{b} = $s;
3615             }
3616         }
3617         if ( $field = $host->field('022') ) {
3618             my $s = $field->as_string('a');
3619             if ($s) {
3620                 $sfd{x} = $s;
3621             }
3622         }
3623         if ( $field = $host->field('020') ) {
3624             my $s = $field->as_string('a');
3625             if ($s) {
3626                 $sfd{z} = $s;
3627             }
3628         }
3629         if ( $field = $host->field('001') ) {
3630             $sfd{w} = $field->data(),;
3631         }
3632         $host_field = MARC::Field->new( 773, '0', ' ', %sfd );
3633         return $host_field;
3634     }
3635     elsif ( $marcflavour eq 'UNIMARC' ) {
3636         #author
3637         if ( $field = $host->field('700') || $host->field('710') || $host->field('720') ) {
3638             my $s = $field->as_string('ab');
3639             if ($s) {
3640                 $sfd{a} = $s;
3641             }
3642         }
3643         #title
3644         if ( $field = $host->field('200') ) {
3645             my $s = $field->as_string('a');
3646             if ($s) {
3647                 $sfd{t} = $s;
3648             }
3649         }
3650         #place of publicaton
3651         if ( $field = $host->field('210') ) {
3652             my $s = $field->as_string('a');
3653             if ($s) {
3654                 $sfd{c} = $s;
3655             }
3656         }
3657         #date of publication
3658         if ( $field = $host->field('210') ) {
3659             my $s = $field->as_string('d');
3660             if ($s) {
3661                 $sfd{d} = $s;
3662             }
3663         }
3664         #edition statement
3665         if ( $field = $host->field('205') ) {
3666             my $s = $field->as_string();
3667             if ($s) {
3668                 $sfd{e} = $s;
3669             }
3670         }
3671         #URL
3672         if ( $field = $host->field('856') ) {
3673             my $s = $field->as_string('u');
3674             if ($s) {
3675                 $sfd{u} = $s;
3676             }
3677         }
3678         #ISSN
3679         if ( $field = $host->field('011') ) {
3680             my $s = $field->as_string('a');
3681             if ($s) {
3682                 $sfd{x} = $s;
3683             }
3684         }
3685         #ISBN
3686         if ( $field = $host->field('010') ) {
3687             my $s = $field->as_string('a');
3688             if ($s) {
3689                 $sfd{y} = $s;
3690             }
3691         }
3692         if ( $field = $host->field('001') ) {
3693             $sfd{0} = $field->data(),;
3694         }
3695         $host_field = MARC::Field->new( 461, '0', ' ', %sfd );
3696         return $host_field;
3697     }
3698     return;
3699 }
3700
3701
3702 =head2 UpdateTotalIssues
3703
3704   UpdateTotalIssues($biblionumber, $increase, [$value])
3705
3706 Update the total issue count for a particular bib record.
3707
3708 =over 4
3709
3710 =item C<$biblionumber> is the biblionumber of the bib to update
3711
3712 =item C<$increase> is the amount to increase (or decrease) the total issues count by
3713
3714 =item C<$value> is the absolute value that total issues count should be set to. If provided, C<$increase> is ignored.
3715
3716 =back
3717
3718 =cut
3719
3720 sub UpdateTotalIssues {
3721     my ($biblionumber, $increase, $value) = @_;
3722     my $totalissues;
3723
3724     my $record = GetMarcBiblio($biblionumber);
3725     unless ($record) {
3726         carp "UpdateTotalIssues could not get biblio record";
3727         return;
3728     }
3729     my $data = GetBiblioData($biblionumber);
3730     unless ($data) {
3731         carp "UpdateTotalIssues could not get datas of biblio";
3732         return;
3733     }
3734     my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField('biblioitems.totalissues', $data->{'frameworkcode'});
3735     unless ($totalissuestag) {
3736         return 1; # There is nothing to do
3737     }
3738
3739     if (defined $value) {
3740         $totalissues = $value;
3741     } else {
3742         $totalissues = $data->{'totalissues'} + $increase;
3743     }
3744
3745      my $field = $record->field($totalissuestag);
3746      if (defined $field) {
3747          $field->update( $totalissuessubfield => $totalissues );
3748      } else {
3749          $field = MARC::Field->new($totalissuestag, '0', '0',
3750                  $totalissuessubfield => $totalissues);
3751          $record->insert_grouped_field($field);
3752      }
3753
3754      return ModBiblio($record, $biblionumber, $data->{'frameworkcode'});
3755 }
3756
3757 =head2 RemoveAllNsb
3758
3759     &RemoveAllNsb($record);
3760
3761 Removes all nsb/nse chars from a record
3762
3763 =cut
3764
3765 sub RemoveAllNsb {
3766     my $record = shift;
3767     if (!$record) {
3768         carp 'RemoveAllNsb called with undefined record';
3769         return;
3770     }
3771
3772     SetUTF8Flag($record);
3773
3774     foreach my $field ($record->fields()) {
3775         if ($field->is_control_field()) {
3776             $field->update(nsb_clean($field->data()));
3777         } else {
3778             my @subfields = $field->subfields();
3779             my @new_subfields;
3780             foreach my $subfield (@subfields) {
3781                 push @new_subfields, $subfield->[0] => nsb_clean($subfield->[1]);
3782             }
3783             if (scalar(@new_subfields) > 0) {
3784                 my $new_field;
3785                 eval {
3786                     $new_field = MARC::Field->new(
3787                         $field->tag(),
3788                         $field->indicator(1),
3789                         $field->indicator(2),
3790                         @new_subfields
3791                     );
3792                 };
3793                 if ($@) {
3794                     warn "error in RemoveAllNsb : $@";
3795                 } else {
3796                     $field->replace_with($new_field);
3797                 }
3798             }
3799         }
3800     }
3801
3802     return $record;
3803 }
3804
3805 1;
3806
3807
3808 __END__
3809
3810 =head1 AUTHOR
3811
3812 Koha Development Team <http://koha-community.org/>
3813
3814 Paul POULAIN paul.poulain@free.fr
3815
3816 Joshua Ferraro jmf@liblime.com
3817
3818 =cut