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