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