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