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