Merge remote branch 'kc/new/bug_4276' into kcmaster
[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     my $pages     = '';
1125     my $titletype = 'b';
1126
1127     # For the purposes of generating COinS metadata, LDR/06-07 can be
1128     # considered the same for UNIMARC and MARC21
1129     my $fmts6;
1130     my $fmts7;
1131     %$fmts6 = (
1132                 'a' => 'book',
1133                 'b' => 'manuscript',
1134                 'c' => 'book',
1135                 'd' => 'manuscript',
1136                 'e' => 'map',
1137                 'f' => 'map',
1138                 'g' => 'film',
1139                 'i' => 'audioRecording',
1140                 'j' => 'audioRecording',
1141                 'k' => 'artwork',
1142                 'l' => 'document',
1143                 'm' => 'computerProgram',
1144                 'o' => 'document',
1145                 'r' => 'document',
1146             );
1147     %$fmts7 = (
1148                     'a' => 'journalArticle',
1149                     's' => 'journal',
1150               );
1151
1152     $genre = $fmts6->{$pos6} ? $fmts6->{$pos6} : 'book';
1153
1154     if ( $genre eq 'book' ) {
1155             $genre = $fmts7->{$pos7} if $fmts7->{$pos7};
1156     }
1157
1158     ##### We must transform mtx to a valable mtx and document type ####
1159     if ( $genre eq 'book' ) {
1160             $mtx = 'book';
1161     } elsif ( $genre eq 'journal' ) {
1162             $mtx = 'journal';
1163             $titletype = 'j';
1164     } elsif ( $genre eq 'journalArticle' ) {
1165             $mtx   = 'journal';
1166             $genre = 'article';
1167             $titletype = 'a';
1168     } else {
1169             $mtx = 'dc';
1170     }
1171
1172     $genre = ( $mtx eq 'dc' ) ? "&amp;rft.type=$genre" : "&amp;rft.genre=$genre";
1173
1174     if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
1175
1176         # Setting datas
1177         $aulast  = $record->subfield( '700', 'a' );
1178         $aufirst = $record->subfield( '700', 'b' );
1179         $oauthors = "&amp;rft.au=$aufirst $aulast";
1180
1181         # others authors
1182         if ( $record->field('200') ) {
1183             for my $au ( $record->field('200')->subfield('g') ) {
1184                 $oauthors .= "&amp;rft.au=$au";
1185             }
1186         }
1187         $title =
1188           ( $mtx eq 'dc' )
1189           ? "&amp;rft.title=" . $record->subfield( '200', 'a' )
1190           : "&amp;rft.title=" . $record->subfield( '200', 'a' ) . "&amp;rft.btitle=" . $record->subfield( '200', 'a' );
1191         $pubyear   = $record->subfield( '210', 'd' );
1192         $publisher = $record->subfield( '210', 'c' );
1193         $isbn      = $record->subfield( '010', 'a' );
1194         $issn      = $record->subfield( '011', 'a' );
1195     } else {
1196
1197         # MARC21 need some improve
1198
1199         # Setting datas
1200         if ( $record->field('100') ) {
1201             $oauthors .= "&amp;rft.au=" . $record->subfield( '100', 'a' );
1202         }
1203
1204         # others authors
1205         if ( $record->field('700') ) {
1206             for my $au ( $record->field('700')->subfield('a') ) {
1207                 $oauthors .= "&amp;rft.au=$au";
1208             }
1209         }
1210         $title = "&amp;rft." . $titletype . "title=" . $record->subfield( '245', 'a' );
1211         $subtitle = $record->subfield( '245', 'b' ) || '';
1212         $title .= $subtitle;
1213         if ($titletype eq 'a') {
1214             $pubyear   = substr $record->field('008')->data(), 7, 4;
1215             $isbn      = $record->subfield( '773', 'z' ) || '';
1216             $issn      = $record->subfield( '773', 'x' ) || '';
1217             if ($mtx eq 'journal') {
1218                 $title    .= "&amp;rft.title=" . (($record->subfield( '773', 't' ) || $record->subfield( '773', 'a')));
1219             } else {
1220                 $title    .= "&amp;rft.btitle=" . (($record->subfield( '773', 't' ) || $record->subfield( '773', 'a')) || '');
1221             }
1222             foreach my $rel ($record->subfield( '773', 'g' )) {
1223                 if ($pages) {
1224                     $pages .= ', ';
1225                 }
1226                 $pages .= $rel;
1227             }
1228         } else {
1229             $pubyear   = $record->subfield( '260', 'c' ) || '';
1230             $publisher = $record->subfield( '260', 'b' ) || '';
1231             $isbn      = $record->subfield( '020', 'a' ) || '';
1232             $issn      = $record->subfield( '022', 'a' ) || '';
1233         }
1234
1235     }
1236     my $coins_value =
1237 "ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3A$mtx$genre$title&amp;rft.isbn=$isbn&amp;rft.issn=$issn&amp;rft.aulast=$aulast&amp;rft.aufirst=$aufirst$oauthors&amp;rft.pub=$publisher&amp;rft.date=$pubyear&amp;rft.pages=$pages";
1238     $coins_value =~ s/(\ |&[^a])/\+/g;
1239     $coins_value =~ s/\"/\&quot\;/g;
1240
1241 #<!-- 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="
1242
1243     return $coins_value;
1244 }
1245
1246 =head2 GetAuthorisedValueDesc
1247
1248   my $subfieldvalue =get_authorised_value_desc(
1249     $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category, $opac);
1250
1251 Retrieve the complete description for a given authorised value.
1252
1253 Now takes $category and $value pair too.
1254
1255   my $auth_value_desc =GetAuthorisedValueDesc(
1256     '','', 'DVD' ,'','','CCODE');
1257
1258 If the optional $opac parameter is set to a true value, displays OPAC 
1259 descriptions rather than normal ones when they exist.
1260
1261 =cut
1262
1263 sub GetAuthorisedValueDesc {
1264     my ( $tag, $subfield, $value, $framework, $tagslib, $category, $opac ) = @_;
1265     my $dbh = C4::Context->dbh;
1266
1267     if ( !$category ) {
1268
1269         return $value unless defined $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1270
1271         #---- branch
1272         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1273             return C4::Branch::GetBranchName($value);
1274         }
1275
1276         #---- itemtypes
1277         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1278             return getitemtypeinfo($value)->{description};
1279         }
1280
1281         #---- "true" authorized value
1282         $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1283     }
1284
1285     if ( $category ne "" ) {
1286         my $sth = $dbh->prepare( "SELECT lib, lib_opac FROM authorised_values WHERE category = ? AND authorised_value = ?" );
1287         $sth->execute( $category, $value );
1288         my $data = $sth->fetchrow_hashref;
1289         return ( $opac && $data->{'lib_opac'} ) ? $data->{'lib_opac'} : $data->{'lib'};
1290     } else {
1291         return $value;    # if nothing is found return the original value
1292     }
1293 }
1294
1295 =head2 GetMarcControlnumber
1296
1297   $marccontrolnumber = GetMarcControlnumber($record,$marcflavour);
1298
1299 Get the control number / record Identifier from the MARC record and return it.
1300
1301 =cut
1302
1303 sub GetMarcControlnumber {
1304     my ( $record, $marcflavour ) = @_;
1305     my $controlnumber = "";
1306     # Control number or Record identifier are the same field in MARC21 and UNIMARC
1307     # Keep $marcflavour for possible later use
1308     if ($marcflavour eq "MARC21" || $marcflavour eq "UNIMARC") {
1309         my $controlnumberField = $record->field('001');
1310         if ($controlnumberField) {
1311             $controlnumber = $controlnumberField->data();
1312         }
1313     }
1314     return $controlnumber;
1315 }
1316
1317 =head2 GetMarcISBN
1318
1319   $marcisbnsarray = GetMarcISBN( $record, $marcflavour );
1320
1321 Get all ISBNs from the MARC record and returns them in an array.
1322 ISBNs stored in differents places depending on MARC flavour
1323
1324 =cut
1325
1326 sub GetMarcISBN {
1327     my ( $record, $marcflavour ) = @_;
1328     my $scope;
1329     if ( $marcflavour eq "MARC21" ) {
1330         $scope = '020';
1331     } else {    # assume unimarc if not marc21
1332         $scope = '010';
1333     }
1334     my @marcisbns;
1335     my $isbn = "";
1336     my $tag  = "";
1337     my $marcisbn;
1338     foreach my $field ( $record->field($scope) ) {
1339         my $value = $field->as_string();
1340         if ( $isbn ne "" ) {
1341             $marcisbn = { marcisbn => $isbn, };
1342             push @marcisbns, $marcisbn;
1343             $isbn = $value;
1344         }
1345         if ( $isbn ne $value ) {
1346             $isbn = $isbn . " " . $value;
1347         }
1348     }
1349
1350     if ($isbn) {
1351         $marcisbn = { marcisbn => $isbn };
1352         push @marcisbns, $marcisbn;    #load last tag into array
1353     }
1354     return \@marcisbns;
1355 }    # end GetMarcISBN
1356
1357 =head2 GetMarcNotes
1358
1359   $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1360
1361 Get all notes from the MARC record and returns them in an array.
1362 The note are stored in differents places depending on MARC flavour
1363
1364 =cut
1365
1366 sub GetMarcNotes {
1367     my ( $record, $marcflavour ) = @_;
1368     my $scope;
1369     if ( $marcflavour eq "MARC21" ) {
1370         $scope = '5..';
1371     } else {    # assume unimarc if not marc21
1372         $scope = '3..';
1373     }
1374     my @marcnotes;
1375     my $note = "";
1376     my $tag  = "";
1377     my $marcnote;
1378     foreach my $field ( $record->field($scope) ) {
1379         my $value = $field->as_string();
1380         if ( $note ne "" ) {
1381             $marcnote = { marcnote => $note, };
1382             push @marcnotes, $marcnote;
1383             $note = $value;
1384         }
1385         if ( $note ne $value ) {
1386             $note = $note . " " . $value;
1387         }
1388     }
1389
1390     if ($note) {
1391         $marcnote = { marcnote => $note };
1392         push @marcnotes, $marcnote;    #load last tag into array
1393     }
1394     return \@marcnotes;
1395 }    # end GetMarcNotes
1396
1397 =head2 GetMarcSubjects
1398
1399   $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1400
1401 Get all subjects from the MARC record and returns them in an array.
1402 The subjects are stored in differents places depending on MARC flavour
1403
1404 =cut
1405
1406 sub GetMarcSubjects {
1407     my ( $record, $marcflavour ) = @_;
1408     my ( $mintag, $maxtag );
1409     if ( $marcflavour eq "MARC21" ) {
1410         $mintag = "600";
1411         $maxtag = "699";
1412     } else {    # assume unimarc if not marc21
1413         $mintag = "600";
1414         $maxtag = "611";
1415     }
1416
1417     my @marcsubjects;
1418     my $subject  = "";
1419     my $subfield = "";
1420     my $marcsubject;
1421
1422     my $subject_limit = C4::Context->preference("TraceCompleteSubfields") ? 'su,complete-subfield' : 'su';
1423
1424     foreach my $field ( $record->field('6..') ) {
1425         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1426         my @subfields_loop;
1427         my @subfields = $field->subfields();
1428         my $counter   = 0;
1429         my @link_loop;
1430
1431         # if there is an authority link, build the link with an= subfield9
1432         my $found9 = 0;
1433         for my $subject_subfield (@subfields) {
1434
1435             # don't load unimarc subfields 3,4,5
1436             next if ( ( $marcflavour eq "UNIMARC" ) and ( $subject_subfield->[0] =~ /2|3|4|5/ ) );
1437
1438             # don't load MARC21 subfields 2 (FIXME: any more subfields??)
1439             next if ( ( $marcflavour eq "MARC21" ) and ( $subject_subfield->[0] =~ /2/ ) );
1440             my $code      = $subject_subfield->[0];
1441             my $value     = $subject_subfield->[1];
1442             my $linkvalue = $value;
1443             $linkvalue =~ s/(\(|\))//g;
1444             my $operator = " and " unless $counter == 0;
1445             if ( $code eq 9 ) {
1446                 $found9 = 1;
1447                 @link_loop = ( { 'limit' => 'an', link => "$linkvalue" } );
1448             }
1449             if ( not $found9 ) {
1450                 push @link_loop, { 'limit' => $subject_limit, link => $linkvalue, operator => $operator };
1451             }
1452             my $separator = C4::Context->preference("authoritysep") unless $counter == 0;
1453
1454             # ignore $9
1455             my @this_link_loop = @link_loop;
1456             push @subfields_loop, { code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator } unless ( $subject_subfield->[0] eq 9 );
1457             $counter++;
1458         }
1459
1460         push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
1461
1462     }
1463     return \@marcsubjects;
1464 }    #end getMARCsubjects
1465
1466 =head2 GetMarcAuthors
1467
1468   authors = GetMarcAuthors($record,$marcflavour);
1469
1470 Get all authors from the MARC record and returns them in an array.
1471 The authors are stored in differents places depending on MARC flavour
1472
1473 =cut
1474
1475 sub GetMarcAuthors {
1476     my ( $record, $marcflavour ) = @_;
1477     my ( $mintag, $maxtag );
1478
1479     # tagslib useful for UNIMARC author reponsabilities
1480     my $tagslib =
1481       &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.
1482     if ( $marcflavour eq "MARC21" ) {
1483         $mintag = "700";
1484         $maxtag = "720";
1485     } elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
1486         $mintag = "700";
1487         $maxtag = "712";
1488     } else {
1489         return;
1490     }
1491     my @marcauthors;
1492
1493     foreach my $field ( $record->fields ) {
1494         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1495         my @subfields_loop;
1496         my @link_loop;
1497         my @subfields  = $field->subfields();
1498         my $count_auth = 0;
1499
1500         # if there is an authority link, build the link with Koha-Auth-Number: subfield9
1501         my $subfield9 = $field->subfield('9');
1502         for my $authors_subfield (@subfields) {
1503
1504             # don't load unimarc subfields 3, 5
1505             next if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /3|5/ ) );
1506             my $subfieldcode = $authors_subfield->[0];
1507             my $value        = $authors_subfield->[1];
1508             my $linkvalue    = $value;
1509             $linkvalue =~ s/(\(|\))//g;
1510             my $operator = " and " unless $count_auth == 0;
1511
1512             # if we have an authority link, use that as the link, otherwise use standard searching
1513             if ($subfield9) {
1514                 @link_loop = ( { 'limit' => 'an', link => "$subfield9" } );
1515             } else {
1516
1517                 # reset $linkvalue if UNIMARC author responsibility
1518                 if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] eq "4" ) ) {
1519                     $linkvalue = "(" . GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ) . ")";
1520                 }
1521                 push @link_loop, { 'limit' => 'au', link => $linkvalue, operator => $operator };
1522             }
1523             $value = GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib )
1524               if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /4/ ) );
1525             my @this_link_loop = @link_loop;
1526             my $separator = C4::Context->preference("authoritysep") unless $count_auth == 0;
1527             push @subfields_loop, { code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator } unless ( $authors_subfield->[0] eq '9' );
1528             $count_auth++;
1529         }
1530         push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
1531     }
1532     return \@marcauthors;
1533 }
1534
1535 =head2 GetMarcUrls
1536
1537   $marcurls = GetMarcUrls($record,$marcflavour);
1538
1539 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1540 Assumes web resources (not uncommon in MARC21 to omit resource type ind) 
1541
1542 =cut
1543
1544 sub GetMarcUrls {
1545     my ( $record, $marcflavour ) = @_;
1546
1547     my @marcurls;
1548     for my $field ( $record->field('856') ) {
1549         my @notes;
1550         for my $note ( $field->subfield('z') ) {
1551             push @notes, { note => $note };
1552         }
1553         my @urls = $field->subfield('u');
1554         foreach my $url (@urls) {
1555             my $marcurl;
1556             if ( $marcflavour eq 'MARC21' ) {
1557                 my $s3   = $field->subfield('3');
1558                 my $link = $field->subfield('y');
1559                 unless ( $url =~ /^\w+:/ ) {
1560                     if ( $field->indicator(1) eq '7' ) {
1561                         $url = $field->subfield('2') . "://" . $url;
1562                     } elsif ( $field->indicator(1) eq '1' ) {
1563                         $url = 'ftp://' . $url;
1564                     } else {
1565
1566                         #  properly, this should be if ind1=4,
1567                         #  however we will assume http protocol since we're building a link.
1568                         $url = 'http://' . $url;
1569                     }
1570                 }
1571
1572                 # TODO handle ind 2 (relationship)
1573                 $marcurl = {
1574                     MARCURL => $url,
1575                     notes   => \@notes,
1576                 };
1577                 $marcurl->{'linktext'} = $link || $s3 || C4::Context->preference('URLLinkText') || $url;
1578                 $marcurl->{'part'} = $s3 if ($link);
1579                 $marcurl->{'toc'} = 1 if ( defined($s3) && $s3 =~ /^[Tt]able/ );
1580             } else {
1581                 $marcurl->{'linktext'} = $field->subfield('2') || C4::Context->preference('URLLinkText') || $url;
1582                 $marcurl->{'MARCURL'} = $url;
1583             }
1584             push @marcurls, $marcurl;
1585         }
1586     }
1587     return \@marcurls;
1588 }
1589
1590 =head2 GetMarcSeries
1591
1592   $marcseriesarray = GetMarcSeries($record,$marcflavour);
1593
1594 Get all series from the MARC record and returns them in an array.
1595 The series are stored in differents places depending on MARC flavour
1596
1597 =cut
1598
1599 sub GetMarcSeries {
1600     my ( $record, $marcflavour ) = @_;
1601     my ( $mintag, $maxtag );
1602     if ( $marcflavour eq "MARC21" ) {
1603         $mintag = "440";
1604         $maxtag = "490";
1605     } else {    # assume unimarc if not marc21
1606         $mintag = "600";
1607         $maxtag = "619";
1608     }
1609
1610     my @marcseries;
1611     my $subjct   = "";
1612     my $subfield = "";
1613     my $marcsubjct;
1614
1615     foreach my $field ( $record->field('440'), $record->field('490') ) {
1616         my @subfields_loop;
1617
1618         #my $value = $field->subfield('a');
1619         #$marcsubjct = {MARCSUBJCT => $value,};
1620         my @subfields = $field->subfields();
1621
1622         #warn "subfields:".join " ", @$subfields;
1623         my $counter = 0;
1624         my @link_loop;
1625         for my $series_subfield (@subfields) {
1626             my $volume_number;
1627             undef $volume_number;
1628
1629             # see if this is an instance of a volume
1630             if ( $series_subfield->[0] eq 'v' ) {
1631                 $volume_number = 1;
1632             }
1633
1634             my $code      = $series_subfield->[0];
1635             my $value     = $series_subfield->[1];
1636             my $linkvalue = $value;
1637             $linkvalue =~ s/(\(|\))//g;
1638             my $operator = " and " unless $counter == 0;
1639             push @link_loop, { link => $linkvalue, operator => $operator };
1640             my $separator = C4::Context->preference("authoritysep") unless $counter == 0;
1641             if ($volume_number) {
1642                 push @subfields_loop, { volumenum => $value };
1643             } else {
1644                 push @subfields_loop, { code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number } unless ( $series_subfield->[0] eq '9' );
1645             }
1646             $counter++;
1647         }
1648         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1649
1650         #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1651         #push @marcsubjcts, $marcsubjct;
1652         #$subjct = $value;
1653
1654     }
1655     my $marcseriessarray = \@marcseries;
1656     return $marcseriessarray;
1657 }    #end getMARCseriess
1658
1659 =head2 GetFrameworkCode
1660
1661   $frameworkcode = GetFrameworkCode( $biblionumber )
1662
1663 =cut
1664
1665 sub GetFrameworkCode {
1666     my ($biblionumber) = @_;
1667     my $dbh            = C4::Context->dbh;
1668     my $sth            = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
1669     $sth->execute($biblionumber);
1670     my ($frameworkcode) = $sth->fetchrow;
1671     return $frameworkcode;
1672 }
1673
1674 =head2 TransformKohaToMarc
1675
1676     $record = TransformKohaToMarc( $hash )
1677
1678 This function builds partial MARC::Record from a hash
1679 Hash entries can be from biblio or biblioitems.
1680
1681 This function is called in acquisition module, to create a basic catalogue entry from user entry
1682
1683 =cut
1684
1685 sub TransformKohaToMarc {
1686     my ($hash) = @_;
1687     my $sth    = C4::Context->dbh->prepare( "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?" );
1688     my $record = MARC::Record->new();
1689     SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
1690     foreach ( keys %{$hash} ) {
1691         &TransformKohaToMarcOneField( $sth, $record, $_, $hash->{$_}, '' );
1692     }
1693     return $record;
1694 }
1695
1696 =head2 TransformKohaToMarcOneField
1697
1698     $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
1699
1700 =cut
1701
1702 sub TransformKohaToMarcOneField {
1703     my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
1704     $frameworkcode = '' unless $frameworkcode;
1705     my $tagfield;
1706     my $tagsubfield;
1707
1708     if ( !defined $sth ) {
1709         my $dbh = C4::Context->dbh;
1710         $sth = $dbh->prepare( "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?" );
1711     }
1712     $sth->execute( $frameworkcode, $kohafieldname );
1713     if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
1714         my @values = split(/\s?\|\s?/, $value, -1);
1715         
1716         foreach my $itemvalue (@values){
1717         my $tag = $record->field($tagfield);
1718         if ($tag) {
1719                 $tag->add_subfields( $tagsubfield => $itemvalue );
1720             $record->delete_field($tag);
1721             $record->insert_fields_ordered($tag);
1722             }
1723             else {
1724                 $record->add_fields( $tagfield, " ", " ", $tagsubfield => $itemvalue );
1725             }
1726         }
1727     }
1728     return $record;
1729 }
1730
1731 =head2 TransformHtmlToXml
1732
1733   $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, 
1734                              $ind_tag, $auth_type )
1735
1736 $auth_type contains :
1737
1738 =over
1739
1740 =item - nothing : rebuild a biblio. In UNIMARC the encoding is in 100$a pos 26/27
1741
1742 =item - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
1743
1744 =item - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
1745
1746 =back
1747
1748 =cut
1749
1750 sub TransformHtmlToXml {
1751     my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
1752     my $xml = MARC::File::XML::header('UTF-8');
1753     $xml .= "<record>\n";
1754     $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
1755     MARC::File::XML->default_record_format($auth_type);
1756
1757     # in UNIMARC, field 100 contains the encoding
1758     # check that there is one, otherwise the
1759     # MARC::Record->new_from_xml will fail (and Koha will die)
1760     my $unimarc_and_100_exist = 0;
1761     $unimarc_and_100_exist = 1 if $auth_type eq 'ITEM';    # if we rebuild an item, no need of a 100 field
1762     my $prevvalue;
1763     my $prevtag = -1;
1764     my $first   = 1;
1765     my $j       = -1;
1766     for ( my $i = 0 ; $i < @$tags ; $i++ ) {
1767
1768         if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a" ) {
1769
1770             # if we have a 100 field and it's values are not correct, skip them.
1771             # if we don't have any valid 100 field, we will create a default one at the end
1772             my $enc = substr( @$values[$i], 26, 2 );
1773             if ( $enc eq '01' or $enc eq '50' or $enc eq '03' ) {
1774                 $unimarc_and_100_exist = 1;
1775             } else {
1776                 next;
1777             }
1778         }
1779         @$values[$i] =~ s/&/&amp;/g;
1780         @$values[$i] =~ s/</&lt;/g;
1781         @$values[$i] =~ s/>/&gt;/g;
1782         @$values[$i] =~ s/"/&quot;/g;
1783         @$values[$i] =~ s/'/&apos;/g;
1784
1785         #         if ( !utf8::is_utf8( @$values[$i] ) ) {
1786         #             utf8::decode( @$values[$i] );
1787         #         }
1788         if ( ( @$tags[$i] ne $prevtag ) ) {
1789             $j++ unless ( @$tags[$i] eq "" );
1790             my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) };
1791             my $indicator2 = eval { substr( @$indicator[$j], 1, 1 ) };
1792             my $ind1       = _default_ind_to_space($indicator1);
1793             my $ind2;
1794             if ( @$indicator[$j] ) {
1795                 $ind2 = _default_ind_to_space($indicator2);
1796             } else {
1797                 warn "Indicator in @$tags[$i] is empty";
1798                 $ind2 = " ";
1799             }
1800             if ( !$first ) {
1801                 $xml .= "</datafield>\n";
1802                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
1803                     && ( @$values[$i] ne "" ) ) {
1804                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1805                     $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1806                     $first = 0;
1807                 } else {
1808                     $first = 1;
1809                 }
1810             } else {
1811                 if ( @$values[$i] ne "" ) {
1812
1813                     # leader
1814                     if ( @$tags[$i] eq "000" ) {
1815                         $xml .= "<leader>@$values[$i]</leader>\n";
1816                         $first = 1;
1817
1818                         # rest of the fixed fields
1819                     } elsif ( @$tags[$i] < 10 ) {
1820                         $xml .= "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
1821                         $first = 1;
1822                     } else {
1823                         $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1824                         $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1825                         $first = 0;
1826                     }
1827                 }
1828             }
1829         } else {    # @$tags[$i] eq $prevtag
1830             my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) };
1831             my $indicator2 = eval { substr( @$indicator[$j], 1, 1 ) };
1832             my $ind1       = _default_ind_to_space($indicator1);
1833             my $ind2;
1834             if ( @$indicator[$j] ) {
1835                 $ind2 = _default_ind_to_space($indicator2);
1836             } else {
1837                 warn "Indicator in @$tags[$i] is empty";
1838                 $ind2 = " ";
1839             }
1840             if ( @$values[$i] eq "" ) {
1841             } else {
1842                 if ($first) {
1843                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1844                     $first = 0;
1845                 }
1846                 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1847             }
1848         }
1849         $prevtag = @$tags[$i];
1850     }
1851     $xml .= "</datafield>\n" if @$tags > 0;
1852     if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist ) {
1853
1854         #     warn "SETTING 100 for $auth_type";
1855         my $string = strftime( "%Y%m%d", localtime(time) );
1856
1857         # set 50 to position 26 is biblios, 13 if authorities
1858         my $pos = 26;
1859         $pos = 13 if $auth_type eq 'UNIMARCAUTH';
1860         $string = sprintf( "%-*s", 35, $string );
1861         substr( $string, $pos, 6, "50" );
1862         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
1863         $xml .= "<subfield code=\"a\">$string</subfield>\n";
1864         $xml .= "</datafield>\n";
1865     }
1866     $xml .= "</record>\n";
1867     $xml .= MARC::File::XML::footer();
1868     return $xml;
1869 }
1870
1871 =head2 _default_ind_to_space
1872
1873 Passed what should be an indicator returns a space
1874 if its undefined or zero length
1875
1876 =cut
1877
1878 sub _default_ind_to_space {
1879     my $s = shift;
1880     if ( !defined $s || $s eq q{} ) {
1881         return ' ';
1882     }
1883     return $s;
1884 }
1885
1886 =head2 TransformHtmlToMarc
1887
1888     L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
1889     L<$params> is a ref to an array as below:
1890     {
1891         'tag_010_indicator1_531951' ,
1892         'tag_010_indicator2_531951' ,
1893         'tag_010_code_a_531951_145735' ,
1894         'tag_010_subfield_a_531951_145735' ,
1895         'tag_200_indicator1_873510' ,
1896         'tag_200_indicator2_873510' ,
1897         'tag_200_code_a_873510_673465' ,
1898         'tag_200_subfield_a_873510_673465' ,
1899         'tag_200_code_b_873510_704318' ,
1900         'tag_200_subfield_b_873510_704318' ,
1901         'tag_200_code_e_873510_280822' ,
1902         'tag_200_subfield_e_873510_280822' ,
1903         'tag_200_code_f_873510_110730' ,
1904         'tag_200_subfield_f_873510_110730' ,
1905     }
1906     L<$cgi> is the CGI object which containts the value.
1907     L<$record> is the MARC::Record object.
1908
1909 =cut
1910
1911 sub TransformHtmlToMarc {
1912     my $params = shift;
1913     my $cgi    = shift;
1914
1915     # explicitly turn on the UTF-8 flag for all
1916     # 'tag_' parameters to avoid incorrect character
1917     # conversion later on
1918     my $cgi_params = $cgi->Vars;
1919     foreach my $param_name ( keys %$cgi_params ) {
1920         if ( $param_name =~ /^tag_/ ) {
1921             my $param_value = $cgi_params->{$param_name};
1922             if ( utf8::decode($param_value) ) {
1923                 $cgi_params->{$param_name} = $param_value;
1924             }
1925
1926             # FIXME - need to do something if string is not valid UTF-8
1927         }
1928     }
1929
1930     # creating a new record
1931     my $record = MARC::Record->new();
1932     my $i      = 0;
1933     my @fields;
1934     while ( $params->[$i] ) {    # browse all CGI params
1935         my $param    = $params->[$i];
1936         my $newfield = 0;
1937
1938         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
1939         if ( $param eq 'biblionumber' ) {
1940             my ( $biblionumbertagfield, $biblionumbertagsubfield ) = &GetMarcFromKohaField( "biblio.biblionumber", '' );
1941             if ( $biblionumbertagfield < 10 ) {
1942                 $newfield = MARC::Field->new( $biblionumbertagfield, $cgi->param($param), );
1943             } else {
1944                 $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => $cgi->param($param), );
1945             }
1946             push @fields, $newfield if ($newfield);
1947         } elsif ( $param =~ /^tag_(\d*)_indicator1_/ ) {    # new field start when having 'input name="..._indicator1_..."
1948             my $tag = $1;
1949
1950             my $ind1 = _default_ind_to_space( substr( $cgi->param($param), 0, 1 ) );
1951             my $ind2 = _default_ind_to_space( substr( $cgi->param( $params->[ $i + 1 ] ), 0, 1 ) );
1952             $newfield = 0;
1953             my $j = $i + 2;
1954
1955             if ( $tag < 10 ) {                              # no code for theses fields
1956                                                             # in MARC editor, 000 contains the leader.
1957                 if ( $tag eq '000' ) {
1958                     # Force a fake leader even if not provided to avoid crashing
1959                     # during decoding MARC record containing UTF-8 characters
1960                     $record->leader(
1961                         length( $cgi->param($params->[$j+1]) ) == 24
1962                         ? $cgi->param( $params->[ $j + 1 ] )
1963                         : '     nam a22        4500'
1964                         )
1965                     ;
1966                     # between 001 and 009 (included)
1967                 } elsif ( $cgi->param( $params->[ $j + 1 ] ) ne '' ) {
1968                     $newfield = MARC::Field->new( $tag, $cgi->param( $params->[ $j + 1 ] ), );
1969                 }
1970
1971                 # > 009, deal with subfields
1972             } else {
1973                 while ( defined $params->[$j] && $params->[$j] =~ /_code_/ ) {    # browse all it's subfield
1974                     my $inner_param = $params->[$j];
1975                     if ($newfield) {
1976                         if ( $cgi->param( $params->[ $j + 1 ] ) ne '' ) {         # only if there is a value (code => value)
1977                             $newfield->add_subfields( $cgi->param($inner_param) => $cgi->param( $params->[ $j + 1 ] ) );
1978                         }
1979                     } else {
1980                         if ( $cgi->param( $params->[ $j + 1 ] ) ne '' ) {         # creating only if there is a value (code => value)
1981                             $newfield = MARC::Field->new( $tag, $ind1, $ind2, $cgi->param($inner_param) => $cgi->param( $params->[ $j + 1 ] ), );
1982                         }
1983                     }
1984                     $j += 2;
1985                 }
1986             }
1987             push @fields, $newfield if ($newfield);
1988         }
1989         $i++;
1990     }
1991
1992     $record->append_fields(@fields);
1993     return $record;
1994 }
1995
1996 # cache inverted MARC field map
1997 our $inverted_field_map;
1998
1999 =head2 TransformMarcToKoha
2000
2001   $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2002
2003 Extract data from a MARC bib record into a hashref representing
2004 Koha biblio, biblioitems, and items fields. 
2005
2006 =cut
2007
2008 sub TransformMarcToKoha {
2009     my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
2010
2011     my $result;
2012     $limit_table = $limit_table || 0;
2013     $frameworkcode = '' unless defined $frameworkcode;
2014
2015     unless ( defined $inverted_field_map ) {
2016         $inverted_field_map = _get_inverted_marc_field_map();
2017     }
2018
2019     my %tables = ();
2020     if ( defined $limit_table && $limit_table eq 'items' ) {
2021         $tables{'items'} = 1;
2022     } else {
2023         $tables{'items'}       = 1;
2024         $tables{'biblio'}      = 1;
2025         $tables{'biblioitems'} = 1;
2026     }
2027
2028     # traverse through record
2029   MARCFIELD: foreach my $field ( $record->fields() ) {
2030         my $tag = $field->tag();
2031         next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
2032         if ( $field->is_control_field() ) {
2033             my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
2034           ENTRY: foreach my $entry ( @{$kohafields} ) {
2035                 my ( $subfield, $table, $column ) = @{$entry};
2036                 next ENTRY unless exists $tables{$table};
2037                 my $key = _disambiguate( $table, $column );
2038                 if ( $result->{$key} ) {
2039                     unless ( ( $key eq "biblionumber" or $key eq "biblioitemnumber" ) and ( $field->data() eq "" ) ) {
2040                         $result->{$key} .= " | " . $field->data();
2041                     }
2042                 } else {
2043                     $result->{$key} = $field->data();
2044                 }
2045             }
2046         } else {
2047
2048             # deal with subfields
2049           MARCSUBFIELD: foreach my $sf ( $field->subfields() ) {
2050                 my $code = $sf->[0];
2051                 next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
2052                 my $value = $sf->[1];
2053               SFENTRY: foreach my $entry ( @{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} } ) {
2054                     my ( $table, $column ) = @{$entry};
2055                     next SFENTRY unless exists $tables{$table};
2056                     my $key = _disambiguate( $table, $column );
2057                     if ( $result->{$key} ) {
2058                         unless ( ( $key eq "biblionumber" or $key eq "biblioitemnumber" ) and ( $value eq "" ) ) {
2059                             $result->{$key} .= " | " . $value;
2060                         }
2061                     } else {
2062                         $result->{$key} = $value;
2063                     }
2064                 }
2065             }
2066         }
2067     }
2068
2069     # modify copyrightdate to keep only the 1st year found
2070     if ( exists $result->{'copyrightdate'} ) {
2071         my $temp = $result->{'copyrightdate'};
2072         $temp =~ m/c(\d\d\d\d)/;
2073         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) {    # search cYYYY first
2074             $result->{'copyrightdate'} = $1;
2075         } else {                                       # if no cYYYY, get the 1st date.
2076             $temp =~ m/(\d\d\d\d)/;
2077             $result->{'copyrightdate'} = $1;
2078         }
2079     }
2080
2081     # modify publicationyear to keep only the 1st year found
2082     if ( exists $result->{'publicationyear'} ) {
2083         my $temp = $result->{'publicationyear'};
2084         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) {    # search cYYYY first
2085             $result->{'publicationyear'} = $1;
2086         } else {                                       # if no cYYYY, get the 1st date.
2087             $temp =~ m/(\d\d\d\d)/;
2088             $result->{'publicationyear'} = $1;
2089         }
2090     }
2091
2092     return $result;
2093 }
2094
2095 sub _get_inverted_marc_field_map {
2096     my $field_map = {};
2097     my $relations = C4::Context->marcfromkohafield;
2098
2099     foreach my $frameworkcode ( keys %{$relations} ) {
2100         foreach my $kohafield ( keys %{ $relations->{$frameworkcode} } ) {
2101             next unless @{ $relations->{$frameworkcode}->{$kohafield} };    # not all columns are mapped to MARC tag & subfield
2102             my $tag      = $relations->{$frameworkcode}->{$kohafield}->[0];
2103             my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
2104             my ( $table, $column ) = split /[.]/, $kohafield, 2;
2105             push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
2106             push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
2107         }
2108     }
2109     return $field_map;
2110 }
2111
2112 =head2 _disambiguate
2113
2114   $newkey = _disambiguate($table, $field);
2115
2116 This is a temporary hack to distinguish between the
2117 following sets of columns when using TransformMarcToKoha.
2118
2119   items.cn_source & biblioitems.cn_source
2120   items.cn_sort & biblioitems.cn_sort
2121
2122 Columns that are currently NOT distinguished (FIXME
2123 due to lack of time to fully test) are:
2124
2125   biblio.notes and biblioitems.notes
2126   biblionumber
2127   timestamp
2128   biblioitemnumber
2129
2130 FIXME - this is necessary because prefixing each column
2131 name with the table name would require changing lots
2132 of code and templates, and exposing more of the DB
2133 structure than is good to the UI templates, particularly
2134 since biblio and bibloitems may well merge in a future
2135 version.  In the future, it would also be good to 
2136 separate DB access and UI presentation field names
2137 more.
2138
2139 =cut
2140
2141 sub CountItemsIssued {
2142     my ($biblionumber) = @_;
2143     my $dbh            = C4::Context->dbh;
2144     my $sth            = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
2145     $sth->execute($biblionumber);
2146     my $row = $sth->fetchrow_hashref();
2147     return $row->{'issuedCount'};
2148 }
2149
2150 sub _disambiguate {
2151     my ( $table, $column ) = @_;
2152     if ( $column eq "cn_sort" or $column eq "cn_source" ) {
2153         return $table . '.' . $column;
2154     } else {
2155         return $column;
2156     }
2157
2158 }
2159
2160 =head2 get_koha_field_from_marc
2161
2162   $result->{_disambiguate($table, $field)} = 
2163      get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2164
2165 Internal function to map data from the MARC record to a specific non-MARC field.
2166 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2167
2168 =cut
2169
2170 sub get_koha_field_from_marc {
2171     my ( $koha_table, $koha_column, $record, $frameworkcode ) = @_;
2172     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table . '.' . $koha_column, $frameworkcode );
2173     my $kohafield;
2174     foreach my $field ( $record->field($tagfield) ) {
2175         if ( $field->tag() < 10 ) {
2176             if ($kohafield) {
2177                 $kohafield .= " | " . $field->data();
2178             } else {
2179                 $kohafield = $field->data();
2180             }
2181         } else {
2182             if ( $field->subfields ) {
2183                 my @subfields = $field->subfields();
2184                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2185                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2186                         if ($kohafield) {
2187                             $kohafield .= " | " . $subfields[$subfieldcount][1];
2188                         } else {
2189                             $kohafield = $subfields[$subfieldcount][1];
2190                         }
2191                     }
2192                 }
2193             }
2194         }
2195     }
2196     return $kohafield;
2197 }
2198
2199 =head2 TransformMarcToKohaOneField
2200
2201   $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2202
2203 =cut
2204
2205 sub TransformMarcToKohaOneField {
2206
2207     # FIXME ? if a field has a repeatable subfield that is used in old-db,
2208     # only the 1st will be retrieved...
2209     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2210     my $res = "";
2211     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $kohatable . "." . $kohafield, $frameworkcode );
2212     foreach my $field ( $record->field($tagfield) ) {
2213         if ( $field->tag() < 10 ) {
2214             if ( $result->{$kohafield} ) {
2215                 $result->{$kohafield} .= " | " . $field->data();
2216             } else {
2217                 $result->{$kohafield} = $field->data();
2218             }
2219         } else {
2220             if ( $field->subfields ) {
2221                 my @subfields = $field->subfields();
2222                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2223                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2224                         if ( $result->{$kohafield} ) {
2225                             $result->{$kohafield} .= " | " . $subfields[$subfieldcount][1];
2226                         } else {
2227                             $result->{$kohafield} = $subfields[$subfieldcount][1];
2228                         }
2229                     }
2230                 }
2231             }
2232         }
2233     }
2234     return $result;
2235 }
2236
2237 =head1  OTHER FUNCTIONS
2238
2239
2240 =head2 PrepareItemrecordDisplay
2241
2242   PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
2243
2244 Returns a hash with all the fields for Display a given item data in a template
2245
2246 The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
2247
2248 =cut
2249
2250 sub PrepareItemrecordDisplay {
2251
2252     my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2253
2254     my $dbh = C4::Context->dbh;
2255     $frameworkcode = &GetFrameworkCode($bibnum) if $bibnum;
2256     my ( $itemtagfield, $itemtagsubfield ) = &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2257     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2258
2259     # return nothing if we don't have found an existing framework.
2260     return "" unless $tagslib;
2261     my $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum ) if ($itemnum);
2262     my @loop_data;
2263     my $authorised_values_sth = $dbh->prepare( "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib" );
2264     foreach my $tag ( sort keys %{$tagslib} ) {
2265         my $previous_tag = '';
2266         if ( $tag ne '' ) {
2267
2268             # loop through each subfield
2269             my $cntsubf;
2270             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2271                 next if ( subfield_is_koha_internal_p($subfield) );
2272                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2273                 my %subfield_data;
2274                 $subfield_data{tag}           = $tag;
2275                 $subfield_data{subfield}      = $subfield;
2276                 $subfield_data{countsubfield} = $cntsubf++;
2277                 $subfield_data{kohafield}     = $tagslib->{$tag}->{$subfield}->{'kohafield'};
2278
2279                 #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2280                 $subfield_data{marc_lib}   = $tagslib->{$tag}->{$subfield}->{lib};
2281                 $subfield_data{mandatory}  = $tagslib->{$tag}->{$subfield}->{mandatory};
2282                 $subfield_data{repeatable} = $tagslib->{$tag}->{$subfield}->{repeatable};
2283                 $subfield_data{hidden}     = "display:none"
2284                   if $tagslib->{$tag}->{$subfield}->{hidden};
2285                 my ( $x, $defaultvalue );
2286                 if ($itemrecord) {
2287                     ( $x, $defaultvalue ) = _find_value( $tag, $subfield, $itemrecord );
2288                 }
2289                 $defaultvalue = $tagslib->{$tag}->{$subfield}->{defaultvalue} unless $defaultvalue;
2290                 if ( !defined $defaultvalue ) {
2291                     $defaultvalue = q||;
2292                 }
2293                 $defaultvalue =~ s/"/&quot;/g;
2294
2295                 # search for itemcallnumber if applicable
2296                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2297                     && C4::Context->preference('itemcallnumber') ) {
2298                     my $CNtag      = substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2299                     my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2300                     my $temp = $itemrecord->field($CNtag) if ($itemrecord);
2301                     if ($temp) {
2302                         $defaultvalue = $temp->subfield($CNsubfield);
2303                     }
2304                 }
2305                 if (   $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2306                     && $defaultvalues
2307                     && $defaultvalues->{'callnumber'} ) {
2308                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2309                     unless ($temp) {
2310                         $defaultvalue = $defaultvalues->{'callnumber'} if $defaultvalues;
2311                     }
2312                 }
2313                 if (   ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' )
2314                     && $defaultvalues
2315                     && $defaultvalues->{'branchcode'} ) {
2316                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2317                     unless ($temp) {
2318                         $defaultvalue = $defaultvalues->{branchcode} if $defaultvalues;
2319                     }
2320                 }
2321                 if (   ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.location' )
2322                     && $defaultvalues
2323                     && $defaultvalues->{'location'} ) {
2324                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2325                     unless ($temp) {
2326                         $defaultvalue = $defaultvalues->{location} if $defaultvalues;
2327                     }
2328                 }
2329                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2330                     my @authorised_values;
2331                     my %authorised_lib;
2332
2333                     # builds list, depending on authorised value...
2334                     #---- branch
2335                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
2336                         if (   ( C4::Context->preference("IndependantBranches") )
2337                             && ( C4::Context->userenv->{flags} % 2 != 1 ) ) {
2338                             my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" );
2339                             $sth->execute( C4::Context->userenv->{branch} );
2340                             push @authorised_values, ""
2341                               unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2342                             while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2343                                 push @authorised_values, $branchcode;
2344                                 $authorised_lib{$branchcode} = $branchname;
2345                             }
2346                         } else {
2347                             my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches ORDER BY branchname" );
2348                             $sth->execute;
2349                             push @authorised_values, ""
2350                               unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2351                             while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2352                                 push @authorised_values, $branchcode;
2353                                 $authorised_lib{$branchcode} = $branchname;
2354                             }
2355                         }
2356
2357                         #----- itemtypes
2358                     } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "itemtypes" ) {
2359                         my $sth = $dbh->prepare( "SELECT itemtype,description FROM itemtypes ORDER BY description" );
2360                         $sth->execute;
2361                         push @authorised_values, ""
2362                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2363                         while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) {
2364                             push @authorised_values, $itemtype;
2365                             $authorised_lib{$itemtype} = $description;
2366                         }
2367                         #---- class_sources
2368                     } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "cn_source" ) {
2369                         push @authorised_values, "" unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2370
2371                         my $class_sources = GetClassSources();
2372                         my $default_source = C4::Context->preference("DefaultClassificationSource");
2373
2374                         foreach my $class_source (sort keys %$class_sources) {
2375                             next unless $class_sources->{$class_source}->{'used'} or
2376                                         ($class_source eq $default_source);
2377                             push @authorised_values, $class_source;
2378                             $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'};
2379                         }
2380
2381                         #---- "true" authorised value
2382                     } else {
2383                         $authorised_values_sth->execute( $tagslib->{$tag}->{$subfield}->{authorised_value} );
2384                         push @authorised_values, ""
2385                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2386                         while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
2387                             push @authorised_values, $value;
2388                             $authorised_lib{$value} = $lib;
2389                         }
2390                     }
2391                     $subfield_data{marc_value} = CGI::scrolling_list(
2392                         -name     => 'field_value',
2393                         -values   => \@authorised_values,
2394                         -default  => "$defaultvalue",
2395                         -labels   => \%authorised_lib,
2396                         -size     => 1,
2397                         -tabindex => '',
2398                         -multiple => 0,
2399                     );
2400                 } else {
2401                     $subfield_data{marc_value} = "<input type=\"text\" name=\"field_value\" value=\"$defaultvalue\" size=\"50\" maxlength=\"255\" />";
2402                 }
2403                 push( @loop_data, \%subfield_data );
2404             }
2405         }
2406     }
2407     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2408       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2409     return {
2410         'itemtagfield'    => $itemtagfield,
2411         'itemtagsubfield' => $itemtagsubfield,
2412         'itemnumber'      => $itemnumber,
2413         'iteminformation' => \@loop_data
2414     };
2415 }
2416
2417 #"
2418
2419 #
2420 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2421 # at the same time
2422 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2423 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2424 # =head2 ModZebrafiles
2425 #
2426 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2427 #
2428 # =cut
2429 #
2430 # sub ModZebrafiles {
2431 #
2432 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2433 #
2434 #     my $op;
2435 #     my $zebradir =
2436 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2437 #     unless ( opendir( DIR, "$zebradir" ) ) {
2438 #         warn "$zebradir not found";
2439 #         return;
2440 #     }
2441 #     closedir DIR;
2442 #     my $filename = $zebradir . $biblionumber;
2443 #
2444 #     if ($record) {
2445 #         open( OUTPUT, ">", $filename . ".xml" );
2446 #         print OUTPUT $record;
2447 #         close OUTPUT;
2448 #     }
2449 # }
2450
2451 =head2 ModZebra
2452
2453   ModZebra( $biblionumber, $op, $server, $oldRecord, $newRecord );
2454
2455 $biblionumber is the biblionumber we want to index
2456
2457 $op is specialUpdate or delete, and is used to know what we want to do
2458
2459 $server is the server that we want to update
2460
2461 $oldRecord is the MARC::Record containing the previous version of the record.  This is used only when 
2462 NoZebra=1, as NoZebra indexing needs to know the previous version of a record in order to
2463 do an update.
2464
2465 $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.
2466
2467 =cut
2468
2469 sub ModZebra {
2470 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2471     my ( $biblionumber, $op, $server, $oldRecord, $newRecord ) = @_;
2472     my $dbh = C4::Context->dbh;
2473
2474     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2475     # at the same time
2476     # replaced by a zebraqueue table, that is filled with ModZebra to run.
2477     # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2478
2479     if ( C4::Context->preference("NoZebra") ) {
2480
2481         # lock the nozebra table : we will read index lines, update them in Perl process
2482         # and write everything in 1 transaction.
2483         # lock the table to avoid someone else overwriting what we are doing
2484         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE, auth_subfield_structure READ');
2485         my %result;    # the result hash that will be built by deletion / add, and written on mySQL at the end, to improve speed
2486         if ( $op eq 'specialUpdate' ) {
2487
2488             # OK, we have to add or update the record
2489             # 1st delete (virtually, in indexes), if record actually exists
2490             if ($oldRecord) {
2491                 %result = _DelBiblioNoZebra( $biblionumber, $oldRecord, $server );
2492             }
2493
2494             # ... add the record
2495             %result = _AddBiblioNoZebra( $biblionumber, $newRecord, $server, %result );
2496         } else {
2497
2498             # it's a deletion, delete the record...
2499             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2500             %result = _DelBiblioNoZebra( $biblionumber, $oldRecord, $server );
2501         }
2502
2503         # ok, now update the database...
2504         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2505         foreach my $key ( keys %result ) {
2506             foreach my $index ( keys %{ $result{$key} } ) {
2507                 $sth->execute( $result{$key}->{$index}, $server, $key, $index );
2508             }
2509         }
2510         $dbh->do('UNLOCK TABLES');
2511     } else {
2512
2513         #
2514         # we use zebra, just fill zebraqueue table
2515         #
2516         my $check_sql = "SELECT COUNT(*) FROM zebraqueue 
2517                          WHERE server = ?
2518                          AND   biblio_auth_number = ?
2519                          AND   operation = ?
2520                          AND   done = 0";
2521         my $check_sth = $dbh->prepare_cached($check_sql);
2522         $check_sth->execute( $server, $biblionumber, $op );
2523         my ($count) = $check_sth->fetchrow_array;
2524         $check_sth->finish();
2525         if ( $count == 0 ) {
2526             my $sth = $dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2527             $sth->execute( $biblionumber, $server, $op );
2528             $sth->finish;
2529         }
2530     }
2531 }
2532
2533 =head2 GetNoZebraIndexes
2534
2535   %indexes = GetNoZebraIndexes;
2536
2537 return the data from NoZebraIndexes syspref.
2538
2539 =cut
2540
2541 sub GetNoZebraIndexes {
2542     my $no_zebra_indexes = C4::Context->preference('NoZebraIndexes');
2543     my %indexes;
2544   INDEX: foreach my $line ( split /['"],[\n\r]*/, $no_zebra_indexes ) {
2545         $line =~ /(.*)=>(.*)/;
2546         my $index  = $1;    # initial ' or " is removed afterwards
2547         my $fields = $2;
2548         $index  =~ s/'|"|\s//g;
2549         $fields =~ s/'|"|\s//g;
2550         $indexes{$index} = $fields;
2551     }
2552     return %indexes;
2553 }
2554
2555 =head1 INTERNAL FUNCTIONS
2556
2557 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2558
2559 function to delete a biblio in NoZebra indexes
2560 This function does NOT delete anything in database : it reads all the indexes entries
2561 that have to be deleted & delete them in the hash
2562
2563 The SQL part is done either :
2564  - after the Add if we are modifying a biblio (delete + add again)
2565  - immediatly after this sub if we are doing a true deletion.
2566
2567 $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2568
2569 =cut
2570
2571 sub _DelBiblioNoZebra {
2572     my ( $biblionumber, $record, $server ) = @_;
2573
2574     # Get the indexes
2575     my $dbh = C4::Context->dbh;
2576
2577     # Get the indexes
2578     my %index;
2579     my $title;
2580     if ( $server eq 'biblioserver' ) {
2581         %index = GetNoZebraIndexes;
2582
2583         # get title of the record (to store the 10 first letters with the index)
2584         my ( $titletag, $titlesubfield ) = GetMarcFromKohaField( 'biblio.title', '' );    # FIXME: should be GetFrameworkCode($biblionumber) ??
2585         $title = lc( $record->subfield( $titletag, $titlesubfield ) );
2586     } else {
2587
2588         # for authorities, the "title" is the $a mainentry
2589         my ( $auth_type_tag, $auth_type_sf ) = C4::AuthoritiesMarc::get_auth_type_location();
2590         my $authref = C4::AuthoritiesMarc::GetAuthType( $record->subfield( $auth_type_tag, $auth_type_sf ) );
2591         warn "ERROR : authtype undefined for " . $record->as_formatted unless $authref;
2592         $title = $record->subfield( $authref->{auth_tag_to_report}, 'a' );
2593         $index{'mainmainentry'} = $authref->{'auth_tag_to_report'} . 'a';
2594         $index{'mainentry'}     = $authref->{'auth_tag_to_report'} . '*';
2595         $index{'auth_type'}     = "${auth_type_tag}${auth_type_sf}";
2596     }
2597
2598     my %result;
2599
2600     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2601     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2602
2603     # limit to 10 char, should be enough, and limit the DB size
2604     $title = substr( $title, 0, 10 );
2605
2606     #parse each field
2607     my $sth2 = $dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2608     foreach my $field ( $record->fields() ) {
2609
2610         #parse each subfield
2611         next if $field->tag < 10;
2612         foreach my $subfield ( $field->subfields() ) {
2613             my $tag          = $field->tag();
2614             my $subfieldcode = $subfield->[0];
2615             my $indexed      = 0;
2616
2617             # check each index to see if the subfield is stored somewhere
2618             # otherwise, store it in __RAW__ index
2619             foreach my $key ( keys %index ) {
2620
2621                 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2622                 if ( $index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/ ) {
2623                     $indexed = 1;
2624                     my $line = lc $subfield->[1];
2625
2626                     # remove meaningless value in the field...
2627                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2628
2629                     # ... and split in words
2630                     foreach ( split / /, $line ) {
2631                         next unless $_;    # skip  empty values (multiple spaces)
2632                                            # if the entry is already here, do nothing, the biblionumber has already be removed
2633                         unless ( defined( $result{$key}->{$_} ) && ( $result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/ ) ) {
2634
2635                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2636                             $sth2->execute( $server, $key, $_ );
2637                             my $existing_biblionumbers = $sth2->fetchrow;
2638
2639                             # it exists
2640                             if ($existing_biblionumbers) {
2641
2642                                 #                                 warn " existing for $key $_: $existing_biblionumbers";
2643                                 $result{$key}->{$_} = $existing_biblionumbers;
2644                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2645                             }
2646                         }
2647                     }
2648                 }
2649             }
2650
2651             # the subfield is not indexed, store it in __RAW__ index anyway
2652             unless ($indexed) {
2653                 my $line = lc $subfield->[1];
2654                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2655
2656                 # ... and split in words
2657                 foreach ( split / /, $line ) {
2658                     next unless $_;    # skip  empty values (multiple spaces)
2659                                        # if the entry is already here, do nothing, the biblionumber has already be removed
2660                     unless ( $result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/ ) {
2661
2662                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2663                         $sth2->execute( $server, '__RAW__', $_ );
2664                         my $existing_biblionumbers = $sth2->fetchrow;
2665
2666                         # it exists
2667                         if ($existing_biblionumbers) {
2668                             $result{'__RAW__'}->{$_} = $existing_biblionumbers;
2669                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2670                         }
2671                     }
2672                 }
2673             }
2674         }
2675     }
2676     return %result;
2677 }
2678
2679 =head2 _AddBiblioNoZebra
2680
2681   _AddBiblioNoZebra($biblionumber, $record, $server, %result);
2682
2683 function to add a biblio in NoZebra indexes
2684
2685 =cut
2686
2687 sub _AddBiblioNoZebra {
2688     my ( $biblionumber, $record, $server, %result ) = @_;
2689     my $dbh = C4::Context->dbh;
2690
2691     # Get the indexes
2692     my %index;
2693     my $title;
2694     if ( $server eq 'biblioserver' ) {
2695         %index = GetNoZebraIndexes;
2696
2697         # get title of the record (to store the 10 first letters with the index)
2698         my ( $titletag, $titlesubfield ) = GetMarcFromKohaField( 'biblio.title', '' );    # FIXME: should be GetFrameworkCode($biblionumber) ??
2699         $title = lc( $record->subfield( $titletag, $titlesubfield ) );
2700     } else {
2701
2702         # warn "server : $server";
2703         # for authorities, the "title" is the $a mainentry
2704         my ( $auth_type_tag, $auth_type_sf ) = C4::AuthoritiesMarc::get_auth_type_location();
2705         my $authref = C4::AuthoritiesMarc::GetAuthType( $record->subfield( $auth_type_tag, $auth_type_sf ) );
2706         warn "ERROR : authtype undefined for " . $record->as_formatted unless $authref;
2707         $title = $record->subfield( $authref->{auth_tag_to_report}, 'a' );
2708         $index{'mainmainentry'} = $authref->{auth_tag_to_report} . 'a';
2709         $index{'mainentry'}     = $authref->{auth_tag_to_report} . '*';
2710         $index{'auth_type'}     = "${auth_type_tag}${auth_type_sf}";
2711     }
2712
2713     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2714     $title =~ s/ |\.|,|;|\[|\]|\(|\)|\*|-|'|:|=|\r|\n//g;
2715
2716     # limit to 10 char, should be enough, and limit the DB size
2717     $title = substr( $title, 0, 10 );
2718
2719     #parse each field
2720     my $sth2 = $dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2721     foreach my $field ( $record->fields() ) {
2722
2723         #parse each subfield
2724         ###FIXME: impossible to index a 001-009 value with NoZebra
2725         next if $field->tag < 10;
2726         foreach my $subfield ( $field->subfields() ) {
2727             my $tag          = $field->tag();
2728             my $subfieldcode = $subfield->[0];
2729             my $indexed      = 0;
2730
2731             #             warn "INDEXING :".$subfield->[1];
2732             # check each index to see if the subfield is stored somewhere
2733             # otherwise, store it in __RAW__ index
2734             foreach my $key ( keys %index ) {
2735
2736                 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2737                 if ( $index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/ ) {
2738                     $indexed = 1;
2739                     my $line = lc $subfield->[1];
2740
2741                     # remove meaningless value in the field...
2742                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2743
2744                     # ... and split in words
2745                     foreach ( split / /, $line ) {
2746                         next unless $_;    # skip  empty values (multiple spaces)
2747                                            # if the entry is already here, improve weight
2748
2749                         #                         warn "managing $_";
2750                         if ( exists $result{$key}->{$_} && $result{$key}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d+);/ ) {
2751                             my $weight = $1 + 1;
2752                             $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2753                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2754                         } else {
2755
2756                             # get the value if it exist in the nozebra table, otherwise, create it
2757                             $sth2->execute( $server, $key, $_ );
2758                             my $existing_biblionumbers = $sth2->fetchrow;
2759
2760                             # it exists
2761                             if ($existing_biblionumbers) {
2762                                 $result{$key}->{"$_"} = $existing_biblionumbers;
2763                                 my $weight = defined $1 ? $1 + 1 : 1;
2764                                 $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2765                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2766
2767                                 # create a new ligne for this entry
2768                             } else {
2769
2770                                 #                             warn "INSERT : $server / $key / $_";
2771                                 $dbh->do( 'INSERT INTO nozebra SET server=' . $dbh->quote($server) . ', indexname=' . $dbh->quote($key) . ',value=' . $dbh->quote($_) );
2772                                 $result{$key}->{"$_"} .= "$biblionumber,$title-1;";
2773                             }
2774                         }
2775                     }
2776                 }
2777             }
2778
2779             # the subfield is not indexed, store it in __RAW__ index anyway
2780             unless ($indexed) {
2781                 my $line = lc $subfield->[1];
2782                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2783
2784                 # ... and split in words
2785                 foreach ( split / /, $line ) {
2786                     next unless $_;    # skip  empty values (multiple spaces)
2787                                        # if the entry is already here, improve weight
2788                     my $tmpstr = $result{'__RAW__'}->{"$_"} || "";
2789                     if ( $tmpstr =~ /$biblionumber,\Q$title\E\-(\d+);/ ) {
2790                         my $weight = $1 + 1;
2791                         $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2792                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2793                     } else {
2794
2795                         # get the value if it exist in the nozebra table, otherwise, create it
2796                         $sth2->execute( $server, '__RAW__', $_ );
2797                         my $existing_biblionumbers = $sth2->fetchrow;
2798
2799                         # it exists
2800                         if ($existing_biblionumbers) {
2801                             $result{'__RAW__'}->{"$_"} = $existing_biblionumbers;
2802                             my $weight = ( $1 ? $1 : 0 ) + 1;
2803                             $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2804                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2805
2806                             # create a new ligne for this entry
2807                         } else {
2808                             $dbh->do( 'INSERT INTO nozebra SET server=' . $dbh->quote($server) . ',  indexname="__RAW__",value=' . $dbh->quote($_) );
2809                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-1;";
2810                         }
2811                     }
2812                 }
2813             }
2814         }
2815     }
2816     return %result;
2817 }
2818
2819 =head2 _find_value
2820
2821   ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2822
2823 Find the given $subfield in the given $tag in the given
2824 MARC::Record $record.  If the subfield is found, returns
2825 the (indicators, value) pair; otherwise, (undef, undef) is
2826 returned.
2827
2828 PROPOSITION :
2829 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2830 I suggest we export it from this module.
2831
2832 =cut
2833
2834 sub _find_value {
2835     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2836     my @result;
2837     my $indicator;
2838     if ( $tagfield < 10 ) {
2839         if ( $record->field($tagfield) ) {
2840             push @result, $record->field($tagfield)->data();
2841         } else {
2842             push @result, "";
2843         }
2844     } else {
2845         foreach my $field ( $record->field($tagfield) ) {
2846             my @subfields = $field->subfields();
2847             foreach my $subfield (@subfields) {
2848                 if ( @$subfield[0] eq $insubfield ) {
2849                     push @result, @$subfield[1];
2850                     $indicator = $field->indicator(1) . $field->indicator(2);
2851                 }
2852             }
2853         }
2854     }
2855     return ( $indicator, @result );
2856 }
2857
2858 =head2 _koha_marc_update_bib_ids
2859
2860
2861   _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
2862
2863 Internal function to add or update biblionumber and biblioitemnumber to
2864 the MARC XML.
2865
2866 =cut
2867
2868 sub _koha_marc_update_bib_ids {
2869     my ( $record, $frameworkcode, $biblionumber, $biblioitemnumber ) = @_;
2870
2871     # we must add bibnum and bibitemnum in MARC::Record...
2872     # we build the new field with biblionumber and biblioitemnumber
2873     # we drop the original field
2874     # we add the new builded field.
2875     my ( $biblio_tag,     $biblio_subfield )     = GetMarcFromKohaField( "biblio.biblionumber",          $frameworkcode );
2876     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber", $frameworkcode );
2877
2878     if ( $biblio_tag != $biblioitem_tag ) {
2879
2880         # biblionumber & biblioitemnumber are in different fields
2881
2882         # deal with biblionumber
2883         my ( $new_field, $old_field );
2884         if ( $biblio_tag < 10 ) {
2885             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
2886         } else {
2887             $new_field = MARC::Field->new( $biblio_tag, '', '', "$biblio_subfield" => $biblionumber );
2888         }
2889
2890         # drop old field and create new one...
2891         $old_field = $record->field($biblio_tag);
2892         $record->delete_field($old_field) if $old_field;
2893         $record->append_fields($new_field);
2894
2895         # deal with biblioitemnumber
2896         if ( $biblioitem_tag < 10 ) {
2897             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
2898         } else {
2899             $new_field = MARC::Field->new( $biblioitem_tag, '', '', "$biblioitem_subfield" => $biblioitemnumber, );
2900         }
2901
2902         # drop old field and create new one...
2903         $old_field = $record->field($biblioitem_tag);
2904         $record->delete_field($old_field) if $old_field;
2905         $record->insert_fields_ordered($new_field);
2906
2907     } else {
2908
2909         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
2910         my $new_field = MARC::Field->new(
2911             $biblio_tag, '', '',
2912             "$biblio_subfield"     => $biblionumber,
2913             "$biblioitem_subfield" => $biblioitemnumber
2914         );
2915
2916         # drop old field and create new one...
2917         my $old_field = $record->field($biblio_tag);
2918         $record->delete_field($old_field) if $old_field;
2919         $record->insert_fields_ordered($new_field);
2920     }
2921 }
2922
2923 =head2 _koha_marc_update_biblioitem_cn_sort
2924
2925   _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
2926
2927 Given a MARC bib record and the biblioitem hash, update the
2928 subfield that contains a copy of the value of biblioitems.cn_sort.
2929
2930 =cut
2931
2932 sub _koha_marc_update_biblioitem_cn_sort {
2933     my $marc          = shift;
2934     my $biblioitem    = shift;
2935     my $frameworkcode = shift;
2936
2937     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.cn_sort", $frameworkcode );
2938     return unless $biblioitem_tag;
2939
2940     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2941
2942     if ( my $field = $marc->field($biblioitem_tag) ) {
2943         $field->delete_subfield( code => $biblioitem_subfield );
2944         if ( $cn_sort ne '' ) {
2945             $field->add_subfields( $biblioitem_subfield => $cn_sort );
2946         }
2947     } else {
2948
2949         # if we get here, no biblioitem tag is present in the MARC record, so
2950         # we'll create it if $cn_sort is not empty -- this would be
2951         # an odd combination of events, however
2952         if ($cn_sort) {
2953             $marc->insert_grouped_field( MARC::Field->new( $biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort ) );
2954         }
2955     }
2956 }
2957
2958 =head2 _koha_add_biblio
2959
2960   my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
2961
2962 Internal function to add a biblio ($biblio is a hash with the values)
2963
2964 =cut
2965
2966 sub _koha_add_biblio {
2967     my ( $dbh, $biblio, $frameworkcode ) = @_;
2968
2969     my $error;
2970
2971     # set the series flag
2972     unless (defined $biblio->{'serial'}){
2973         $biblio->{'serial'} = 0;
2974         if ( $biblio->{'seriestitle'} ) { $biblio->{'serial'} = 1 }
2975     }
2976
2977     my $query = "INSERT INTO biblio
2978         SET frameworkcode = ?,
2979             author = ?,
2980             title = ?,
2981             unititle =?,
2982             notes = ?,
2983             serial = ?,
2984             seriestitle = ?,
2985             copyrightdate = ?,
2986             datecreated=NOW(),
2987             abstract = ?
2988         ";
2989     my $sth = $dbh->prepare($query);
2990     $sth->execute(
2991         $frameworkcode, $biblio->{'author'},      $biblio->{'title'},         $biblio->{'unititle'}, $biblio->{'notes'},
2992         $biblio->{'serial'},        $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}
2993     );
2994
2995     my $biblionumber = $dbh->{'mysql_insertid'};
2996     if ( $dbh->errstr ) {
2997         $error .= "ERROR in _koha_add_biblio $query" . $dbh->errstr;
2998         warn $error;
2999     }
3000
3001     $sth->finish();
3002
3003     #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3004     return ( $biblionumber, $error );
3005 }
3006
3007 =head2 _koha_modify_biblio
3008
3009   my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3010
3011 Internal function for updating the biblio table
3012
3013 =cut
3014
3015 sub _koha_modify_biblio {
3016     my ( $dbh, $biblio, $frameworkcode ) = @_;
3017     my $error;
3018
3019     my $query = "
3020         UPDATE biblio
3021         SET    frameworkcode = ?,
3022                author = ?,
3023                title = ?,
3024                unititle = ?,
3025                notes = ?,
3026                serial = ?,
3027                seriestitle = ?,
3028                copyrightdate = ?,
3029                abstract = ?
3030         WHERE  biblionumber = ?
3031         "
3032       ;
3033     my $sth = $dbh->prepare($query);
3034
3035     $sth->execute(
3036         $frameworkcode,      $biblio->{'author'},      $biblio->{'title'},         $biblio->{'unititle'}, $biblio->{'notes'},
3037         $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}, $biblio->{'biblionumber'}
3038     ) if $biblio->{'biblionumber'};
3039
3040     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3041         $error .= "ERROR in _koha_modify_biblio $query" . $dbh->errstr;
3042         warn $error;
3043     }
3044     return ( $biblio->{'biblionumber'}, $error );
3045 }
3046
3047 =head2 _koha_modify_biblioitem_nonmarc
3048
3049   my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3050
3051 Updates biblioitems row except for marc and marcxml, which should be changed
3052 via ModBiblioMarc
3053
3054 =cut
3055
3056 sub _koha_modify_biblioitem_nonmarc {
3057     my ( $dbh, $biblioitem ) = @_;
3058     my $error;
3059
3060     # re-calculate the cn_sort, it may have changed
3061     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3062
3063     my $query = "UPDATE biblioitems 
3064     SET biblionumber    = ?,
3065         volume          = ?,
3066         number          = ?,
3067         itemtype        = ?,
3068         isbn            = ?,
3069         issn            = ?,
3070         publicationyear = ?,
3071         publishercode   = ?,
3072         volumedate      = ?,
3073         volumedesc      = ?,
3074         collectiontitle = ?,
3075         collectionissn  = ?,
3076         collectionvolume= ?,
3077         editionstatement= ?,
3078         editionresponsibility = ?,
3079         illus           = ?,
3080         pages           = ?,
3081         notes           = ?,
3082         size            = ?,
3083         place           = ?,
3084         lccn            = ?,
3085         url             = ?,
3086         cn_source       = ?,
3087         cn_class        = ?,
3088         cn_item         = ?,
3089         cn_suffix       = ?,
3090         cn_sort         = ?,
3091         totalissues     = ?
3092         where biblioitemnumber = ?
3093         ";
3094     my $sth = $dbh->prepare($query);
3095     $sth->execute(
3096         $biblioitem->{'biblionumber'},     $biblioitem->{'volume'},           $biblioitem->{'number'},                $biblioitem->{'itemtype'},
3097         $biblioitem->{'isbn'},             $biblioitem->{'issn'},             $biblioitem->{'publicationyear'},       $biblioitem->{'publishercode'},
3098         $biblioitem->{'volumedate'},       $biblioitem->{'volumedesc'},       $biblioitem->{'collectiontitle'},       $biblioitem->{'collectionissn'},
3099         $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
3100         $biblioitem->{'pages'},            $biblioitem->{'bnotes'},           $biblioitem->{'size'},                  $biblioitem->{'place'},
3101         $biblioitem->{'lccn'},             $biblioitem->{'url'},              $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'},
3102         $biblioitem->{'cn_item'},          $biblioitem->{'cn_suffix'},        $cn_sort,                               $biblioitem->{'totalissues'},
3103         $biblioitem->{'biblioitemnumber'}
3104     );
3105     if ( $dbh->errstr ) {
3106         $error .= "ERROR in _koha_modify_biblioitem_nonmarc $query" . $dbh->errstr;
3107         warn $error;
3108     }
3109     return ( $biblioitem->{'biblioitemnumber'}, $error );
3110 }
3111
3112 =head2 _koha_add_biblioitem
3113
3114   my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3115
3116 Internal function to add a biblioitem
3117
3118 =cut
3119
3120 sub _koha_add_biblioitem {
3121     my ( $dbh, $biblioitem ) = @_;
3122     my $error;
3123
3124     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3125     my $query = "INSERT INTO biblioitems SET
3126         biblionumber    = ?,
3127         volume          = ?,
3128         number          = ?,
3129         itemtype        = ?,
3130         isbn            = ?,
3131         issn            = ?,
3132         publicationyear = ?,
3133         publishercode   = ?,
3134         volumedate      = ?,
3135         volumedesc      = ?,
3136         collectiontitle = ?,
3137         collectionissn  = ?,
3138         collectionvolume= ?,
3139         editionstatement= ?,
3140         editionresponsibility = ?,
3141         illus           = ?,
3142         pages           = ?,
3143         notes           = ?,
3144         size            = ?,
3145         place           = ?,
3146         lccn            = ?,
3147         marc            = ?,
3148         url             = ?,
3149         cn_source       = ?,
3150         cn_class        = ?,
3151         cn_item         = ?,
3152         cn_suffix       = ?,
3153         cn_sort         = ?,
3154         totalissues     = ?
3155         ";
3156     my $sth = $dbh->prepare($query);
3157     $sth->execute(
3158         $biblioitem->{'biblionumber'},     $biblioitem->{'volume'},           $biblioitem->{'number'},                $biblioitem->{'itemtype'},
3159         $biblioitem->{'isbn'},             $biblioitem->{'issn'},             $biblioitem->{'publicationyear'},       $biblioitem->{'publishercode'},
3160         $biblioitem->{'volumedate'},       $biblioitem->{'volumedesc'},       $biblioitem->{'collectiontitle'},       $biblioitem->{'collectionissn'},
3161         $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
3162         $biblioitem->{'pages'},            $biblioitem->{'bnotes'},           $biblioitem->{'size'},                  $biblioitem->{'place'},
3163         $biblioitem->{'lccn'},             $biblioitem->{'marc'},             $biblioitem->{'url'},                   $biblioitem->{'biblioitems.cn_source'},
3164         $biblioitem->{'cn_class'},         $biblioitem->{'cn_item'},          $biblioitem->{'cn_suffix'},             $cn_sort,
3165         $biblioitem->{'totalissues'}
3166     );
3167     my $bibitemnum = $dbh->{'mysql_insertid'};
3168
3169     if ( $dbh->errstr ) {
3170         $error .= "ERROR in _koha_add_biblioitem $query" . $dbh->errstr;
3171         warn $error;
3172     }
3173     $sth->finish();
3174     return ( $bibitemnum, $error );
3175 }
3176
3177 =head2 _koha_delete_biblio
3178
3179   $error = _koha_delete_biblio($dbh,$biblionumber);
3180
3181 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3182
3183 C<$dbh> - the database handle
3184
3185 C<$biblionumber> - the biblionumber of the biblio to be deleted
3186
3187 =cut
3188
3189 # FIXME: add error handling
3190
3191 sub _koha_delete_biblio {
3192     my ( $dbh, $biblionumber ) = @_;
3193
3194     # get all the data for this biblio
3195     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3196     $sth->execute($biblionumber);
3197
3198     if ( my $data = $sth->fetchrow_hashref ) {
3199
3200         # save the record in deletedbiblio
3201         # find the fields to save
3202         my $query = "INSERT INTO deletedbiblio SET ";
3203         my @bind  = ();
3204         foreach my $temp ( keys %$data ) {
3205             $query .= "$temp = ?,";
3206             push( @bind, $data->{$temp} );
3207         }
3208
3209         # replace the last , by ",?)"
3210         $query =~ s/\,$//;
3211         my $bkup_sth = $dbh->prepare($query);
3212         $bkup_sth->execute(@bind);
3213         $bkup_sth->finish;
3214
3215         # delete the biblio
3216         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3217         $del_sth->execute($biblionumber);
3218         $del_sth->finish;
3219     }
3220     $sth->finish;
3221     return undef;
3222 }
3223
3224 =head2 _koha_delete_biblioitems
3225
3226   $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3227
3228 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3229
3230 C<$dbh> - the database handle
3231 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3232
3233 =cut
3234
3235 # FIXME: add error handling
3236
3237 sub _koha_delete_biblioitems {
3238     my ( $dbh, $biblioitemnumber ) = @_;
3239
3240     # get all the data for this biblioitem
3241     my $sth = $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3242     $sth->execute($biblioitemnumber);
3243
3244     if ( my $data = $sth->fetchrow_hashref ) {
3245
3246         # save the record in deletedbiblioitems
3247         # find the fields to save
3248         my $query = "INSERT INTO deletedbiblioitems SET ";
3249         my @bind  = ();
3250         foreach my $temp ( keys %$data ) {
3251             $query .= "$temp = ?,";
3252             push( @bind, $data->{$temp} );
3253         }
3254
3255         # replace the last , by ",?)"
3256         $query =~ s/\,$//;
3257         my $bkup_sth = $dbh->prepare($query);
3258         $bkup_sth->execute(@bind);
3259         $bkup_sth->finish;
3260
3261         # delete the biblioitem
3262         my $del_sth = $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3263         $del_sth->execute($biblioitemnumber);
3264         $del_sth->finish;
3265     }
3266     $sth->finish;
3267     return undef;
3268 }
3269
3270 =head1 UNEXPORTED FUNCTIONS
3271
3272 =head2 ModBiblioMarc
3273
3274   &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3275
3276 Add MARC data for a biblio to koha 
3277
3278 Function exported, but should NOT be used, unless you really know what you're doing
3279
3280 =cut
3281
3282 sub ModBiblioMarc {
3283
3284     # pass the MARC::Record to this function, and it will create the records in the marc field
3285     my ( $record, $biblionumber, $frameworkcode ) = @_;
3286     my $dbh    = C4::Context->dbh;
3287     my @fields = $record->fields();
3288     if ( !$frameworkcode ) {
3289         $frameworkcode = "";
3290     }
3291     my $sth = $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3292     $sth->execute( $frameworkcode, $biblionumber );
3293     $sth->finish;
3294     my $encoding = C4::Context->preference("marcflavour");
3295
3296     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3297     if ( $encoding eq "UNIMARC" ) {
3298         my $string = $record->subfield( 100, "a" );
3299         if ( ($string) && ( length( $record->subfield( 100, "a" ) ) == 36 ) ) {
3300             my $f100 = $record->field(100);
3301             $record->delete_field($f100);
3302         } else {
3303             $string = POSIX::strftime( "%Y%m%d", localtime );
3304             $string =~ s/\-//g;
3305             $string = sprintf( "%-*s", 35, $string );
3306         }
3307         substr( $string, 22, 6, "frey50" );
3308         unless ( $record->subfield( 100, "a" ) ) {
3309             $record->insert_grouped_field( MARC::Field->new( 100, "", "", "a" => $string ) );
3310         }
3311     }
3312
3313     #enhancement 5374: update transaction date (005) for marc21/unimarc
3314     if($encoding =~ /MARC21|UNIMARC/) {
3315       my @a= (localtime) [5,4,3,2,1,0]; $a[0]+=1900; $a[1]++;
3316         # YY MM DD HH MM SS (update year and month)
3317       my $f005= $record->field('005');
3318       $f005->update(sprintf("%4d%02d%02d%02d%02d%04.1f",@a)) if $f005;
3319     }
3320
3321     my $oldRecord;
3322     if ( C4::Context->preference("NoZebra") ) {
3323
3324         # only NoZebra indexing needs to have
3325         # the previous version of the record
3326         $oldRecord = GetMarcBiblio($biblionumber);
3327     }
3328     $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
3329     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding), $biblionumber );
3330     $sth->finish;
3331     ModZebra( $biblionumber, "specialUpdate", "biblioserver", $oldRecord, $record );
3332     return $biblionumber;
3333 }
3334
3335 =head2 z3950_extended_services
3336
3337   z3950_extended_services($serviceType,$serviceOptions,$record);
3338
3339 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.
3340
3341 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
3342
3343 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
3344
3345  action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
3346
3347 and maybe
3348
3349   recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
3350   syntax => the record syntax (transfer syntax)
3351   databaseName = Database from connection object
3352
3353 To set serviceOptions, call set_service_options($serviceType)
3354
3355 C<$record> the record, if one is needed for the service type
3356
3357 A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
3358
3359 =cut
3360
3361 sub z3950_extended_services {
3362     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
3363
3364     # get our connection object
3365     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
3366
3367     # create a new package object
3368     my $Zpackage = $Zconn->package();
3369
3370     # set our options
3371     $Zpackage->option( action => $action );
3372
3373     if ( $serviceOptions->{'databaseName'} ) {
3374         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
3375     }
3376     if ( $serviceOptions->{'recordIdNumber'} ) {
3377         $Zpackage->option( recordIdNumber => $serviceOptions->{'recordIdNumber'} );
3378     }
3379     if ( $serviceOptions->{'recordIdOpaque'} ) {
3380         $Zpackage->option( recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
3381     }
3382
3383     # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
3384     #if ($serviceType eq 'itemorder') {
3385     #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
3386     #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
3387     #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
3388     #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
3389     #}
3390
3391     if ( $serviceOptions->{record} ) {
3392         $Zpackage->option( record => $serviceOptions->{record} );
3393
3394         # can be xml or marc
3395         if ( $serviceOptions->{'syntax'} ) {
3396             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
3397         }
3398     }
3399
3400     # send the request, handle any exception encountered
3401     eval { $Zpackage->send($serviceType) };
3402     if ( $@ && $@->isa("ZOOM::Exception") ) {
3403         return "error:  " . $@->code() . " " . $@->message() . "\n";
3404     }
3405
3406     # free up package resources
3407     $Zpackage->destroy();
3408 }
3409
3410 =head2 set_service_options
3411
3412   my $serviceOptions = set_service_options($serviceType);
3413
3414 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
3415
3416 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
3417
3418 =cut
3419
3420 sub set_service_options {
3421     my ($serviceType) = @_;
3422     my $serviceOptions;
3423
3424     # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
3425     #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
3426
3427     if ( $serviceType eq 'commit' ) {
3428
3429         # nothing to do
3430     }
3431     if ( $serviceType eq 'create' ) {
3432
3433         # nothing to do
3434     }
3435     if ( $serviceType eq 'drop' ) {
3436         die "ERROR: 'drop' not currently supported (by Zebra)";
3437     }
3438     return $serviceOptions;
3439 }
3440
3441 =head2 get_biblio_authorised_values
3442
3443 find the types and values for all authorised values assigned to this biblio.
3444
3445 parameters:
3446     biblionumber
3447     MARC::Record of the bib
3448
3449 returns: a hashref mapping the authorised value to the value set for this biblionumber
3450
3451   $authorised_values = {
3452                        'Scent'     => 'flowery',
3453                        'Audience'  => 'Young Adult',
3454                        'itemtypes' => 'SER',
3455                         };
3456
3457 Notes: forlibrarian should probably be passed in, and called something different.
3458
3459 =cut
3460
3461 sub get_biblio_authorised_values {
3462     my $biblionumber = shift;
3463     my $record       = shift;
3464
3465     my $forlibrarian  = 1;                                 # are we in staff or opac?
3466     my $frameworkcode = GetFrameworkCode($biblionumber);
3467
3468     my $authorised_values;
3469
3470     my $tagslib = GetMarcStructure( $forlibrarian, $frameworkcode )
3471       or return $authorised_values;
3472
3473     # assume that these entries in the authorised_value table are bibliolevel.
3474     # ones that start with 'item%' are item level.
3475     my $query = q(SELECT distinct authorised_value, kohafield
3476                     FROM marc_subfield_structure
3477                     WHERE authorised_value !=''
3478                       AND (kohafield like 'biblio%'
3479                        OR  kohafield like '') );
3480     my $bibliolevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
3481
3482     foreach my $tag ( keys(%$tagslib) ) {
3483         foreach my $subfield ( keys( %{ $tagslib->{$tag} } ) ) {
3484
3485             # warn "checking $subfield. type is: " . ref $tagslib->{ $tag }{ $subfield };
3486             if ( 'HASH' eq ref $tagslib->{$tag}{$subfield} ) {
3487                 if ( defined $tagslib->{$tag}{$subfield}{'authorised_value'} && exists $bibliolevel_authorised_values->{ $tagslib->{$tag}{$subfield}{'authorised_value'} } ) {
3488                     if ( defined $record->field($tag) ) {
3489                         my $this_subfield_value = $record->field($tag)->subfield($subfield);
3490                         if ( defined $this_subfield_value ) {
3491                             $authorised_values->{ $tagslib->{$tag}{$subfield}{'authorised_value'} } = $this_subfield_value;
3492                         }
3493                     }
3494                 }
3495             }
3496         }
3497     }
3498
3499     # warn ( Data::Dumper->Dump( [ $authorised_values ], [ 'authorised_values' ] ) );
3500     return $authorised_values;
3501 }
3502
3503 1;
3504
3505 __END__
3506
3507 =head1 AUTHOR
3508
3509 Koha Development Team <http://koha-community.org/>
3510
3511 Paul POULAIN paul.poulain@free.fr
3512
3513 Joshua Ferraro jmf@liblime.com
3514
3515 =cut