Merge remote branch 'kc/new/pending_qa/enh/bug_3644' 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 "UNIMARC" ) {
1330         $scope = '010';
1331     } else {    # assume marc21 if not unimarc
1332         $scope = '020';
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 "UNIMARC" ) {
1370         $scope = '3..';
1371     } else {    # assume marc21 if not unimarc
1372         $scope = '5..';
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 "UNIMARC" ) {
1410         $mintag = "600";
1411         $maxtag = "611";
1412     } else {    # assume marc21 if not unimarc
1413         $mintag = "600";
1414         $maxtag = "699";
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 "UNIMARC" ) {
1483         $mintag = "700";
1484         $maxtag = "712";
1485     } elsif ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) { # assume marc21 or normarc if not unimarc
1486         $mintag = "700";
1487         $maxtag = "720";
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 "UNIMARC" ) {
1603         $mintag = "600";
1604         $maxtag = "619";
1605     } else {    # assume marc21 if not unimarc
1606         $mintag = "440";
1607         $maxtag = "490";
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                 } elsif ( $tagslib->{$tag}->{$subfield}->{value_builder} ) {
2401                         # opening plugin
2402                         my $plugin = C4::Context->intranetdir . "/cataloguing/value_builder/" . $tagslib->{$tag}->{$subfield}->{'value_builder'};
2403                         if (do $plugin) {
2404                             my $temp;
2405                             my $extended_param = plugin_parameters( $dbh, $temp, $tagslib, $subfield_data{id}, undef );
2406                             my ( $function_name, $javascript ) = plugin_javascript( $dbh, $temp, $tagslib, $subfield_data{id}, undef );
2407                             $subfield_data{random}     = int(rand(1000000));    # why do we need 2 different randoms?
2408                             my $index_subfield = int(rand(1000000));
2409                             $subfield_data{id} = "tag_".$tag."_subfield_".$subfield."_".$index_subfield;
2410                             $subfield_data{marc_value} = qq[<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255"
2411                                 onfocus="Focus$function_name($subfield_data{random}, '$subfield_data{id}');"
2412                                  onblur=" Blur$function_name($subfield_data{random}, '$subfield_data{id}');" />
2413                                 <a href="#" class="buttonDot" onclick="Clic$function_name('$subfield_data{id}'); return false;" title="Tag Editor">...</a>
2414                                 $javascript];
2415                         } else {
2416                             warn "Plugin Failed: $plugin";
2417                             $subfield_data{marc_value} = qq(<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" />); # supply default input form
2418                         }
2419                 }
2420                 elsif ( $tag eq '' ) {       # it's an hidden field
2421                     $subfield_data{marc_value} = qq(<input type="hidden" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2422                 }
2423                 elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) {   # FIXME: shouldn't input type be "hidden" ?
2424                     $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2425                 }
2426                 elsif ( length($defaultvalue) > 100
2427                             or (C4::Context->preference("marcflavour") eq "UNIMARC" and
2428                                   300 <= $tag && $tag < 400 && $subfield eq 'a' )
2429                             or (C4::Context->preference("marcflavour") eq "MARC21"  and
2430                                   500 <= $tag && $tag < 600                     )
2431                           ) {
2432                     # oversize field (textarea)
2433                     $subfield_data{marc_value} = qq(<textarea tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255">$defaultvalue</textarea>\n");
2434                 } else {
2435                     $subfield_data{marc_value} = "<input type=\"text\" name=\"field_value\" value=\"$defaultvalue\" size=\"50\" maxlength=\"255\" />";
2436                 }
2437                 push( @loop_data, \%subfield_data );
2438             }
2439         }
2440     }
2441     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2442       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2443     return {
2444         'itemtagfield'    => $itemtagfield,
2445         'itemtagsubfield' => $itemtagsubfield,
2446         'itemnumber'      => $itemnumber,
2447         'iteminformation' => \@loop_data
2448     };
2449 }
2450
2451 #"
2452
2453 #
2454 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2455 # at the same time
2456 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2457 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2458 # =head2 ModZebrafiles
2459 #
2460 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2461 #
2462 # =cut
2463 #
2464 # sub ModZebrafiles {
2465 #
2466 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2467 #
2468 #     my $op;
2469 #     my $zebradir =
2470 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2471 #     unless ( opendir( DIR, "$zebradir" ) ) {
2472 #         warn "$zebradir not found";
2473 #         return;
2474 #     }
2475 #     closedir DIR;
2476 #     my $filename = $zebradir . $biblionumber;
2477 #
2478 #     if ($record) {
2479 #         open( OUTPUT, ">", $filename . ".xml" );
2480 #         print OUTPUT $record;
2481 #         close OUTPUT;
2482 #     }
2483 # }
2484
2485 =head2 ModZebra
2486
2487   ModZebra( $biblionumber, $op, $server, $oldRecord, $newRecord );
2488
2489 $biblionumber is the biblionumber we want to index
2490
2491 $op is specialUpdate or delete, and is used to know what we want to do
2492
2493 $server is the server that we want to update
2494
2495 $oldRecord is the MARC::Record containing the previous version of the record.  This is used only when 
2496 NoZebra=1, as NoZebra indexing needs to know the previous version of a record in order to
2497 do an update.
2498
2499 $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.
2500
2501 =cut
2502
2503 sub ModZebra {
2504 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2505     my ( $biblionumber, $op, $server, $oldRecord, $newRecord ) = @_;
2506     my $dbh = C4::Context->dbh;
2507
2508     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2509     # at the same time
2510     # replaced by a zebraqueue table, that is filled with ModZebra to run.
2511     # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2512
2513     if ( C4::Context->preference("NoZebra") ) {
2514
2515         # lock the nozebra table : we will read index lines, update them in Perl process
2516         # and write everything in 1 transaction.
2517         # lock the table to avoid someone else overwriting what we are doing
2518         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE, auth_subfield_structure READ');
2519         my %result;    # the result hash that will be built by deletion / add, and written on mySQL at the end, to improve speed
2520         if ( $op eq 'specialUpdate' ) {
2521
2522             # OK, we have to add or update the record
2523             # 1st delete (virtually, in indexes), if record actually exists
2524             if ($oldRecord) {
2525                 %result = _DelBiblioNoZebra( $biblionumber, $oldRecord, $server );
2526             }
2527
2528             # ... add the record
2529             %result = _AddBiblioNoZebra( $biblionumber, $newRecord, $server, %result );
2530         } else {
2531
2532             # it's a deletion, delete the record...
2533             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2534             %result = _DelBiblioNoZebra( $biblionumber, $oldRecord, $server );
2535         }
2536
2537         # ok, now update the database...
2538         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2539         foreach my $key ( keys %result ) {
2540             foreach my $index ( keys %{ $result{$key} } ) {
2541                 $sth->execute( $result{$key}->{$index}, $server, $key, $index );
2542             }
2543         }
2544         $dbh->do('UNLOCK TABLES');
2545     } else {
2546
2547         #
2548         # we use zebra, just fill zebraqueue table
2549         #
2550         my $check_sql = "SELECT COUNT(*) FROM zebraqueue 
2551                          WHERE server = ?
2552                          AND   biblio_auth_number = ?
2553                          AND   operation = ?
2554                          AND   done = 0";
2555         my $check_sth = $dbh->prepare_cached($check_sql);
2556         $check_sth->execute( $server, $biblionumber, $op );
2557         my ($count) = $check_sth->fetchrow_array;
2558         $check_sth->finish();
2559         if ( $count == 0 ) {
2560             my $sth = $dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2561             $sth->execute( $biblionumber, $server, $op );
2562             $sth->finish;
2563         }
2564     }
2565 }
2566
2567 =head2 GetNoZebraIndexes
2568
2569   %indexes = GetNoZebraIndexes;
2570
2571 return the data from NoZebraIndexes syspref.
2572
2573 =cut
2574
2575 sub GetNoZebraIndexes {
2576     my $no_zebra_indexes = C4::Context->preference('NoZebraIndexes');
2577     my %indexes;
2578   INDEX: foreach my $line ( split /['"],[\n\r]*/, $no_zebra_indexes ) {
2579         $line =~ /(.*)=>(.*)/;
2580         my $index  = $1;    # initial ' or " is removed afterwards
2581         my $fields = $2;
2582         $index  =~ s/'|"|\s//g;
2583         $fields =~ s/'|"|\s//g;
2584         $indexes{$index} = $fields;
2585     }
2586     return %indexes;
2587 }
2588
2589 =head1 INTERNAL FUNCTIONS
2590
2591 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2592
2593 function to delete a biblio in NoZebra indexes
2594 This function does NOT delete anything in database : it reads all the indexes entries
2595 that have to be deleted & delete them in the hash
2596
2597 The SQL part is done either :
2598  - after the Add if we are modifying a biblio (delete + add again)
2599  - immediatly after this sub if we are doing a true deletion.
2600
2601 $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2602
2603 =cut
2604
2605 sub _DelBiblioNoZebra {
2606     my ( $biblionumber, $record, $server ) = @_;
2607
2608     # Get the indexes
2609     my $dbh = C4::Context->dbh;
2610
2611     # Get the indexes
2612     my %index;
2613     my $title;
2614     if ( $server eq 'biblioserver' ) {
2615         %index = GetNoZebraIndexes;
2616
2617         # get title of the record (to store the 10 first letters with the index)
2618         my ( $titletag, $titlesubfield ) = GetMarcFromKohaField( 'biblio.title', '' );    # FIXME: should be GetFrameworkCode($biblionumber) ??
2619         $title = lc( $record->subfield( $titletag, $titlesubfield ) );
2620     } else {
2621
2622         # for authorities, the "title" is the $a mainentry
2623         my ( $auth_type_tag, $auth_type_sf ) = C4::AuthoritiesMarc::get_auth_type_location();
2624         my $authref = C4::AuthoritiesMarc::GetAuthType( $record->subfield( $auth_type_tag, $auth_type_sf ) );
2625         warn "ERROR : authtype undefined for " . $record->as_formatted unless $authref;
2626         $title = $record->subfield( $authref->{auth_tag_to_report}, 'a' );
2627         $index{'mainmainentry'} = $authref->{'auth_tag_to_report'} . 'a';
2628         $index{'mainentry'}     = $authref->{'auth_tag_to_report'} . '*';
2629         $index{'auth_type'}     = "${auth_type_tag}${auth_type_sf}";
2630     }
2631
2632     my %result;
2633
2634     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2635     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2636
2637     # limit to 10 char, should be enough, and limit the DB size
2638     $title = substr( $title, 0, 10 );
2639
2640     #parse each field
2641     my $sth2 = $dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2642     foreach my $field ( $record->fields() ) {
2643
2644         #parse each subfield
2645         next if $field->tag < 10;
2646         foreach my $subfield ( $field->subfields() ) {
2647             my $tag          = $field->tag();
2648             my $subfieldcode = $subfield->[0];
2649             my $indexed      = 0;
2650
2651             # check each index to see if the subfield is stored somewhere
2652             # otherwise, store it in __RAW__ index
2653             foreach my $key ( keys %index ) {
2654
2655                 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2656                 if ( $index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/ ) {
2657                     $indexed = 1;
2658                     my $line = lc $subfield->[1];
2659
2660                     # remove meaningless value in the field...
2661                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2662
2663                     # ... and split in words
2664                     foreach ( split / /, $line ) {
2665                         next unless $_;    # skip  empty values (multiple spaces)
2666                                            # if the entry is already here, do nothing, the biblionumber has already be removed
2667                         unless ( defined( $result{$key}->{$_} ) && ( $result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/ ) ) {
2668
2669                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2670                             $sth2->execute( $server, $key, $_ );
2671                             my $existing_biblionumbers = $sth2->fetchrow;
2672
2673                             # it exists
2674                             if ($existing_biblionumbers) {
2675
2676                                 #                                 warn " existing for $key $_: $existing_biblionumbers";
2677                                 $result{$key}->{$_} = $existing_biblionumbers;
2678                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2679                             }
2680                         }
2681                     }
2682                 }
2683             }
2684
2685             # the subfield is not indexed, store it in __RAW__ index anyway
2686             unless ($indexed) {
2687                 my $line = lc $subfield->[1];
2688                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2689
2690                 # ... and split in words
2691                 foreach ( split / /, $line ) {
2692                     next unless $_;    # skip  empty values (multiple spaces)
2693                                        # if the entry is already here, do nothing, the biblionumber has already be removed
2694                     unless ( $result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/ ) {
2695
2696                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2697                         $sth2->execute( $server, '__RAW__', $_ );
2698                         my $existing_biblionumbers = $sth2->fetchrow;
2699
2700                         # it exists
2701                         if ($existing_biblionumbers) {
2702                             $result{'__RAW__'}->{$_} = $existing_biblionumbers;
2703                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2704                         }
2705                     }
2706                 }
2707             }
2708         }
2709     }
2710     return %result;
2711 }
2712
2713 =head2 _AddBiblioNoZebra
2714
2715   _AddBiblioNoZebra($biblionumber, $record, $server, %result);
2716
2717 function to add a biblio in NoZebra indexes
2718
2719 =cut
2720
2721 sub _AddBiblioNoZebra {
2722     my ( $biblionumber, $record, $server, %result ) = @_;
2723     my $dbh = C4::Context->dbh;
2724
2725     # Get the indexes
2726     my %index;
2727     my $title;
2728     if ( $server eq 'biblioserver' ) {
2729         %index = GetNoZebraIndexes;
2730
2731         # get title of the record (to store the 10 first letters with the index)
2732         my ( $titletag, $titlesubfield ) = GetMarcFromKohaField( 'biblio.title', '' );    # FIXME: should be GetFrameworkCode($biblionumber) ??
2733         $title = lc( $record->subfield( $titletag, $titlesubfield ) );
2734     } else {
2735
2736         # warn "server : $server";
2737         # for authorities, the "title" is the $a mainentry
2738         my ( $auth_type_tag, $auth_type_sf ) = C4::AuthoritiesMarc::get_auth_type_location();
2739         my $authref = C4::AuthoritiesMarc::GetAuthType( $record->subfield( $auth_type_tag, $auth_type_sf ) );
2740         warn "ERROR : authtype undefined for " . $record->as_formatted unless $authref;
2741         $title = $record->subfield( $authref->{auth_tag_to_report}, 'a' );
2742         $index{'mainmainentry'} = $authref->{auth_tag_to_report} . 'a';
2743         $index{'mainentry'}     = $authref->{auth_tag_to_report} . '*';
2744         $index{'auth_type'}     = "${auth_type_tag}${auth_type_sf}";
2745     }
2746
2747     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2748     $title =~ s/ |\.|,|;|\[|\]|\(|\)|\*|-|'|:|=|\r|\n//g;
2749
2750     # limit to 10 char, should be enough, and limit the DB size
2751     $title = substr( $title, 0, 10 );
2752
2753     #parse each field
2754     my $sth2 = $dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2755     foreach my $field ( $record->fields() ) {
2756
2757         #parse each subfield
2758         ###FIXME: impossible to index a 001-009 value with NoZebra
2759         next if $field->tag < 10;
2760         foreach my $subfield ( $field->subfields() ) {
2761             my $tag          = $field->tag();
2762             my $subfieldcode = $subfield->[0];
2763             my $indexed      = 0;
2764
2765             #             warn "INDEXING :".$subfield->[1];
2766             # check each index to see if the subfield is stored somewhere
2767             # otherwise, store it in __RAW__ index
2768             foreach my $key ( keys %index ) {
2769
2770                 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2771                 if ( $index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/ ) {
2772                     $indexed = 1;
2773                     my $line = lc $subfield->[1];
2774
2775                     # remove meaningless value in the field...
2776                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2777
2778                     # ... and split in words
2779                     foreach ( split / /, $line ) {
2780                         next unless $_;    # skip  empty values (multiple spaces)
2781                                            # if the entry is already here, improve weight
2782
2783                         #                         warn "managing $_";
2784                         if ( exists $result{$key}->{$_} && $result{$key}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d+);/ ) {
2785                             my $weight = $1 + 1;
2786                             $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2787                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2788                         } else {
2789
2790                             # get the value if it exist in the nozebra table, otherwise, create it
2791                             $sth2->execute( $server, $key, $_ );
2792                             my $existing_biblionumbers = $sth2->fetchrow;
2793
2794                             # it exists
2795                             if ($existing_biblionumbers) {
2796                                 $result{$key}->{"$_"} = $existing_biblionumbers;
2797                                 my $weight = defined $1 ? $1 + 1 : 1;
2798                                 $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2799                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2800
2801                                 # create a new ligne for this entry
2802                             } else {
2803
2804                                 #                             warn "INSERT : $server / $key / $_";
2805                                 $dbh->do( 'INSERT INTO nozebra SET server=' . $dbh->quote($server) . ', indexname=' . $dbh->quote($key) . ',value=' . $dbh->quote($_) );
2806                                 $result{$key}->{"$_"} .= "$biblionumber,$title-1;";
2807                             }
2808                         }
2809                     }
2810                 }
2811             }
2812
2813             # the subfield is not indexed, store it in __RAW__ index anyway
2814             unless ($indexed) {
2815                 my $line = lc $subfield->[1];
2816                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2817
2818                 # ... and split in words
2819                 foreach ( split / /, $line ) {
2820                     next unless $_;    # skip  empty values (multiple spaces)
2821                                        # if the entry is already here, improve weight
2822                     my $tmpstr = $result{'__RAW__'}->{"$_"} || "";
2823                     if ( $tmpstr =~ /$biblionumber,\Q$title\E\-(\d+);/ ) {
2824                         my $weight = $1 + 1;
2825                         $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2826                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2827                     } else {
2828
2829                         # get the value if it exist in the nozebra table, otherwise, create it
2830                         $sth2->execute( $server, '__RAW__', $_ );
2831                         my $existing_biblionumbers = $sth2->fetchrow;
2832
2833                         # it exists
2834                         if ($existing_biblionumbers) {
2835                             $result{'__RAW__'}->{"$_"} = $existing_biblionumbers;
2836                             my $weight = ( $1 ? $1 : 0 ) + 1;
2837                             $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2838                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2839
2840                             # create a new ligne for this entry
2841                         } else {
2842                             $dbh->do( 'INSERT INTO nozebra SET server=' . $dbh->quote($server) . ',  indexname="__RAW__",value=' . $dbh->quote($_) );
2843                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-1;";
2844                         }
2845                     }
2846                 }
2847             }
2848         }
2849     }
2850     return %result;
2851 }
2852
2853 =head2 _find_value
2854
2855   ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2856
2857 Find the given $subfield in the given $tag in the given
2858 MARC::Record $record.  If the subfield is found, returns
2859 the (indicators, value) pair; otherwise, (undef, undef) is
2860 returned.
2861
2862 PROPOSITION :
2863 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2864 I suggest we export it from this module.
2865
2866 =cut
2867
2868 sub _find_value {
2869     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2870     my @result;
2871     my $indicator;
2872     if ( $tagfield < 10 ) {
2873         if ( $record->field($tagfield) ) {
2874             push @result, $record->field($tagfield)->data();
2875         } else {
2876             push @result, "";
2877         }
2878     } else {
2879         foreach my $field ( $record->field($tagfield) ) {
2880             my @subfields = $field->subfields();
2881             foreach my $subfield (@subfields) {
2882                 if ( @$subfield[0] eq $insubfield ) {
2883                     push @result, @$subfield[1];
2884                     $indicator = $field->indicator(1) . $field->indicator(2);
2885                 }
2886             }
2887         }
2888     }
2889     return ( $indicator, @result );
2890 }
2891
2892 =head2 _koha_marc_update_bib_ids
2893
2894
2895   _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
2896
2897 Internal function to add or update biblionumber and biblioitemnumber to
2898 the MARC XML.
2899
2900 =cut
2901
2902 sub _koha_marc_update_bib_ids {
2903     my ( $record, $frameworkcode, $biblionumber, $biblioitemnumber ) = @_;
2904
2905     # we must add bibnum and bibitemnum in MARC::Record...
2906     # we build the new field with biblionumber and biblioitemnumber
2907     # we drop the original field
2908     # we add the new builded field.
2909     my ( $biblio_tag,     $biblio_subfield )     = GetMarcFromKohaField( "biblio.biblionumber",          $frameworkcode );
2910     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber", $frameworkcode );
2911
2912     if ( $biblio_tag != $biblioitem_tag ) {
2913
2914         # biblionumber & biblioitemnumber are in different fields
2915
2916         # deal with biblionumber
2917         my ( $new_field, $old_field );
2918         if ( $biblio_tag < 10 ) {
2919             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
2920         } else {
2921             $new_field = MARC::Field->new( $biblio_tag, '', '', "$biblio_subfield" => $biblionumber );
2922         }
2923
2924         # drop old field and create new one...
2925         $old_field = $record->field($biblio_tag);
2926         $record->delete_field($old_field) if $old_field;
2927         $record->append_fields($new_field);
2928
2929         # deal with biblioitemnumber
2930         if ( $biblioitem_tag < 10 ) {
2931             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
2932         } else {
2933             $new_field = MARC::Field->new( $biblioitem_tag, '', '', "$biblioitem_subfield" => $biblioitemnumber, );
2934         }
2935
2936         # drop old field and create new one...
2937         $old_field = $record->field($biblioitem_tag);
2938         $record->delete_field($old_field) if $old_field;
2939         $record->insert_fields_ordered($new_field);
2940
2941     } else {
2942
2943         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
2944         my $new_field = MARC::Field->new(
2945             $biblio_tag, '', '',
2946             "$biblio_subfield"     => $biblionumber,
2947             "$biblioitem_subfield" => $biblioitemnumber
2948         );
2949
2950         # drop old field and create new one...
2951         my $old_field = $record->field($biblio_tag);
2952         $record->delete_field($old_field) if $old_field;
2953         $record->insert_fields_ordered($new_field);
2954     }
2955 }
2956
2957 =head2 _koha_marc_update_biblioitem_cn_sort
2958
2959   _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
2960
2961 Given a MARC bib record and the biblioitem hash, update the
2962 subfield that contains a copy of the value of biblioitems.cn_sort.
2963
2964 =cut
2965
2966 sub _koha_marc_update_biblioitem_cn_sort {
2967     my $marc          = shift;
2968     my $biblioitem    = shift;
2969     my $frameworkcode = shift;
2970
2971     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.cn_sort", $frameworkcode );
2972     return unless $biblioitem_tag;
2973
2974     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2975
2976     if ( my $field = $marc->field($biblioitem_tag) ) {
2977         $field->delete_subfield( code => $biblioitem_subfield );
2978         if ( $cn_sort ne '' ) {
2979             $field->add_subfields( $biblioitem_subfield => $cn_sort );
2980         }
2981     } else {
2982
2983         # if we get here, no biblioitem tag is present in the MARC record, so
2984         # we'll create it if $cn_sort is not empty -- this would be
2985         # an odd combination of events, however
2986         if ($cn_sort) {
2987             $marc->insert_grouped_field( MARC::Field->new( $biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort ) );
2988         }
2989     }
2990 }
2991
2992 =head2 _koha_add_biblio
2993
2994   my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
2995
2996 Internal function to add a biblio ($biblio is a hash with the values)
2997
2998 =cut
2999
3000 sub _koha_add_biblio {
3001     my ( $dbh, $biblio, $frameworkcode ) = @_;
3002
3003     my $error;
3004
3005     # set the series flag
3006     unless (defined $biblio->{'serial'}){
3007         $biblio->{'serial'} = 0;
3008         if ( $biblio->{'seriestitle'} ) { $biblio->{'serial'} = 1 }
3009     }
3010
3011     my $query = "INSERT INTO biblio
3012         SET frameworkcode = ?,
3013             author = ?,
3014             title = ?,
3015             unititle =?,
3016             notes = ?,
3017             serial = ?,
3018             seriestitle = ?,
3019             copyrightdate = ?,
3020             datecreated=NOW(),
3021             abstract = ?
3022         ";
3023     my $sth = $dbh->prepare($query);
3024     $sth->execute(
3025         $frameworkcode, $biblio->{'author'},      $biblio->{'title'},         $biblio->{'unititle'}, $biblio->{'notes'},
3026         $biblio->{'serial'},        $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}
3027     );
3028
3029     my $biblionumber = $dbh->{'mysql_insertid'};
3030     if ( $dbh->errstr ) {
3031         $error .= "ERROR in _koha_add_biblio $query" . $dbh->errstr;
3032         warn $error;
3033     }
3034
3035     $sth->finish();
3036
3037     #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3038     return ( $biblionumber, $error );
3039 }
3040
3041 =head2 _koha_modify_biblio
3042
3043   my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3044
3045 Internal function for updating the biblio table
3046
3047 =cut
3048
3049 sub _koha_modify_biblio {
3050     my ( $dbh, $biblio, $frameworkcode ) = @_;
3051     my $error;
3052
3053     my $query = "
3054         UPDATE biblio
3055         SET    frameworkcode = ?,
3056                author = ?,
3057                title = ?,
3058                unititle = ?,
3059                notes = ?,
3060                serial = ?,
3061                seriestitle = ?,
3062                copyrightdate = ?,
3063                abstract = ?
3064         WHERE  biblionumber = ?
3065         "
3066       ;
3067     my $sth = $dbh->prepare($query);
3068
3069     $sth->execute(
3070         $frameworkcode,      $biblio->{'author'},      $biblio->{'title'},         $biblio->{'unititle'}, $biblio->{'notes'},
3071         $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}, $biblio->{'biblionumber'}
3072     ) if $biblio->{'biblionumber'};
3073
3074     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3075         $error .= "ERROR in _koha_modify_biblio $query" . $dbh->errstr;
3076         warn $error;
3077     }
3078     return ( $biblio->{'biblionumber'}, $error );
3079 }
3080
3081 =head2 _koha_modify_biblioitem_nonmarc
3082
3083   my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3084
3085 Updates biblioitems row except for marc and marcxml, which should be changed
3086 via ModBiblioMarc
3087
3088 =cut
3089
3090 sub _koha_modify_biblioitem_nonmarc {
3091     my ( $dbh, $biblioitem ) = @_;
3092     my $error;
3093
3094     # re-calculate the cn_sort, it may have changed
3095     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3096
3097     my $query = "UPDATE biblioitems 
3098     SET biblionumber    = ?,
3099         volume          = ?,
3100         number          = ?,
3101         itemtype        = ?,
3102         isbn            = ?,
3103         issn            = ?,
3104         publicationyear = ?,
3105         publishercode   = ?,
3106         volumedate      = ?,
3107         volumedesc      = ?,
3108         collectiontitle = ?,
3109         collectionissn  = ?,
3110         collectionvolume= ?,
3111         editionstatement= ?,
3112         editionresponsibility = ?,
3113         illus           = ?,
3114         pages           = ?,
3115         notes           = ?,
3116         size            = ?,
3117         place           = ?,
3118         lccn            = ?,
3119         url             = ?,
3120         cn_source       = ?,
3121         cn_class        = ?,
3122         cn_item         = ?,
3123         cn_suffix       = ?,
3124         cn_sort         = ?,
3125         totalissues     = ?
3126         where biblioitemnumber = ?
3127         ";
3128     my $sth = $dbh->prepare($query);
3129     $sth->execute(
3130         $biblioitem->{'biblionumber'},     $biblioitem->{'volume'},           $biblioitem->{'number'},                $biblioitem->{'itemtype'},
3131         $biblioitem->{'isbn'},             $biblioitem->{'issn'},             $biblioitem->{'publicationyear'},       $biblioitem->{'publishercode'},
3132         $biblioitem->{'volumedate'},       $biblioitem->{'volumedesc'},       $biblioitem->{'collectiontitle'},       $biblioitem->{'collectionissn'},
3133         $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
3134         $biblioitem->{'pages'},            $biblioitem->{'bnotes'},           $biblioitem->{'size'},                  $biblioitem->{'place'},
3135         $biblioitem->{'lccn'},             $biblioitem->{'url'},              $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'},
3136         $biblioitem->{'cn_item'},          $biblioitem->{'cn_suffix'},        $cn_sort,                               $biblioitem->{'totalissues'},
3137         $biblioitem->{'biblioitemnumber'}
3138     );
3139     if ( $dbh->errstr ) {
3140         $error .= "ERROR in _koha_modify_biblioitem_nonmarc $query" . $dbh->errstr;
3141         warn $error;
3142     }
3143     return ( $biblioitem->{'biblioitemnumber'}, $error );
3144 }
3145
3146 =head2 _koha_add_biblioitem
3147
3148   my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3149
3150 Internal function to add a biblioitem
3151
3152 =cut
3153
3154 sub _koha_add_biblioitem {
3155     my ( $dbh, $biblioitem ) = @_;
3156     my $error;
3157
3158     my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3159     my $query = "INSERT INTO biblioitems SET
3160         biblionumber    = ?,
3161         volume          = ?,
3162         number          = ?,
3163         itemtype        = ?,
3164         isbn            = ?,
3165         issn            = ?,
3166         publicationyear = ?,
3167         publishercode   = ?,
3168         volumedate      = ?,
3169         volumedesc      = ?,
3170         collectiontitle = ?,
3171         collectionissn  = ?,
3172         collectionvolume= ?,
3173         editionstatement= ?,
3174         editionresponsibility = ?,
3175         illus           = ?,
3176         pages           = ?,
3177         notes           = ?,
3178         size            = ?,
3179         place           = ?,
3180         lccn            = ?,
3181         marc            = ?,
3182         url             = ?,
3183         cn_source       = ?,
3184         cn_class        = ?,
3185         cn_item         = ?,
3186         cn_suffix       = ?,
3187         cn_sort         = ?,
3188         totalissues     = ?
3189         ";
3190     my $sth = $dbh->prepare($query);
3191     $sth->execute(
3192         $biblioitem->{'biblionumber'},     $biblioitem->{'volume'},           $biblioitem->{'number'},                $biblioitem->{'itemtype'},
3193         $biblioitem->{'isbn'},             $biblioitem->{'issn'},             $biblioitem->{'publicationyear'},       $biblioitem->{'publishercode'},
3194         $biblioitem->{'volumedate'},       $biblioitem->{'volumedesc'},       $biblioitem->{'collectiontitle'},       $biblioitem->{'collectionissn'},
3195         $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
3196         $biblioitem->{'pages'},            $biblioitem->{'bnotes'},           $biblioitem->{'size'},                  $biblioitem->{'place'},
3197         $biblioitem->{'lccn'},             $biblioitem->{'marc'},             $biblioitem->{'url'},                   $biblioitem->{'biblioitems.cn_source'},
3198         $biblioitem->{'cn_class'},         $biblioitem->{'cn_item'},          $biblioitem->{'cn_suffix'},             $cn_sort,
3199         $biblioitem->{'totalissues'}
3200     );
3201     my $bibitemnum = $dbh->{'mysql_insertid'};
3202
3203     if ( $dbh->errstr ) {
3204         $error .= "ERROR in _koha_add_biblioitem $query" . $dbh->errstr;
3205         warn $error;
3206     }
3207     $sth->finish();
3208     return ( $bibitemnum, $error );
3209 }
3210
3211 =head2 _koha_delete_biblio
3212
3213   $error = _koha_delete_biblio($dbh,$biblionumber);
3214
3215 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3216
3217 C<$dbh> - the database handle
3218
3219 C<$biblionumber> - the biblionumber of the biblio to be deleted
3220
3221 =cut
3222
3223 # FIXME: add error handling
3224
3225 sub _koha_delete_biblio {
3226     my ( $dbh, $biblionumber ) = @_;
3227
3228     # get all the data for this biblio
3229     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3230     $sth->execute($biblionumber);
3231
3232     if ( my $data = $sth->fetchrow_hashref ) {
3233
3234         # save the record in deletedbiblio
3235         # find the fields to save
3236         my $query = "INSERT INTO deletedbiblio SET ";
3237         my @bind  = ();
3238         foreach my $temp ( keys %$data ) {
3239             $query .= "$temp = ?,";
3240             push( @bind, $data->{$temp} );
3241         }
3242
3243         # replace the last , by ",?)"
3244         $query =~ s/\,$//;
3245         my $bkup_sth = $dbh->prepare($query);
3246         $bkup_sth->execute(@bind);
3247         $bkup_sth->finish;
3248
3249         # delete the biblio
3250         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3251         $del_sth->execute($biblionumber);
3252         $del_sth->finish;
3253     }
3254     $sth->finish;
3255     return undef;
3256 }
3257
3258 =head2 _koha_delete_biblioitems
3259
3260   $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3261
3262 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3263
3264 C<$dbh> - the database handle
3265 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3266
3267 =cut
3268
3269 # FIXME: add error handling
3270
3271 sub _koha_delete_biblioitems {
3272     my ( $dbh, $biblioitemnumber ) = @_;
3273
3274     # get all the data for this biblioitem
3275     my $sth = $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3276     $sth->execute($biblioitemnumber);
3277
3278     if ( my $data = $sth->fetchrow_hashref ) {
3279
3280         # save the record in deletedbiblioitems
3281         # find the fields to save
3282         my $query = "INSERT INTO deletedbiblioitems SET ";
3283         my @bind  = ();
3284         foreach my $temp ( keys %$data ) {
3285             $query .= "$temp = ?,";
3286             push( @bind, $data->{$temp} );
3287         }
3288
3289         # replace the last , by ",?)"
3290         $query =~ s/\,$//;
3291         my $bkup_sth = $dbh->prepare($query);
3292         $bkup_sth->execute(@bind);
3293         $bkup_sth->finish;
3294
3295         # delete the biblioitem
3296         my $del_sth = $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3297         $del_sth->execute($biblioitemnumber);
3298         $del_sth->finish;
3299     }
3300     $sth->finish;
3301     return undef;
3302 }
3303
3304 =head1 UNEXPORTED FUNCTIONS
3305
3306 =head2 ModBiblioMarc
3307
3308   &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3309
3310 Add MARC data for a biblio to koha 
3311
3312 Function exported, but should NOT be used, unless you really know what you're doing
3313
3314 =cut
3315
3316 sub ModBiblioMarc {
3317
3318     # pass the MARC::Record to this function, and it will create the records in the marc field
3319     my ( $record, $biblionumber, $frameworkcode ) = @_;
3320     my $dbh    = C4::Context->dbh;
3321     my @fields = $record->fields();
3322     if ( !$frameworkcode ) {
3323         $frameworkcode = "";
3324     }
3325     my $sth = $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3326     $sth->execute( $frameworkcode, $biblionumber );
3327     $sth->finish;
3328     my $encoding = C4::Context->preference("marcflavour");
3329
3330     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3331     if ( $encoding eq "UNIMARC" ) {
3332         my $string = $record->subfield( 100, "a" );
3333         if ( ($string) && ( length( $record->subfield( 100, "a" ) ) == 36 ) ) {
3334             my $f100 = $record->field(100);
3335             $record->delete_field($f100);
3336         } else {
3337             $string = POSIX::strftime( "%Y%m%d", localtime );
3338             $string =~ s/\-//g;
3339             $string = sprintf( "%-*s", 35, $string );
3340         }
3341         substr( $string, 22, 6, "frey50" );
3342         unless ( $record->subfield( 100, "a" ) ) {
3343             $record->insert_grouped_field( MARC::Field->new( 100, "", "", "a" => $string ) );
3344         }
3345     }
3346
3347     #enhancement 5374: update transaction date (005) for marc21/unimarc
3348     if($encoding =~ /MARC21|UNIMARC/) {
3349       my @a= (localtime) [5,4,3,2,1,0]; $a[0]+=1900; $a[1]++;
3350         # YY MM DD HH MM SS (update year and month)
3351       my $f005= $record->field('005');
3352       $f005->update(sprintf("%4d%02d%02d%02d%02d%04.1f",@a)) if $f005;
3353     }
3354
3355     my $oldRecord;
3356     if ( C4::Context->preference("NoZebra") ) {
3357
3358         # only NoZebra indexing needs to have
3359         # the previous version of the record
3360         $oldRecord = GetMarcBiblio($biblionumber);
3361     }
3362     $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
3363     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding), $biblionumber );
3364     $sth->finish;
3365     ModZebra( $biblionumber, "specialUpdate", "biblioserver", $oldRecord, $record );
3366     return $biblionumber;
3367 }
3368
3369 =head2 z3950_extended_services
3370
3371   z3950_extended_services($serviceType,$serviceOptions,$record);
3372
3373 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.
3374
3375 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
3376
3377 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
3378
3379  action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
3380
3381 and maybe
3382
3383   recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
3384   syntax => the record syntax (transfer syntax)
3385   databaseName = Database from connection object
3386
3387 To set serviceOptions, call set_service_options($serviceType)
3388
3389 C<$record> the record, if one is needed for the service type
3390
3391 A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
3392
3393 =cut
3394
3395 sub z3950_extended_services {
3396     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
3397
3398     # get our connection object
3399     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
3400
3401     # create a new package object
3402     my $Zpackage = $Zconn->package();
3403
3404     # set our options
3405     $Zpackage->option( action => $action );
3406
3407     if ( $serviceOptions->{'databaseName'} ) {
3408         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
3409     }
3410     if ( $serviceOptions->{'recordIdNumber'} ) {
3411         $Zpackage->option( recordIdNumber => $serviceOptions->{'recordIdNumber'} );
3412     }
3413     if ( $serviceOptions->{'recordIdOpaque'} ) {
3414         $Zpackage->option( recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
3415     }
3416
3417     # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
3418     #if ($serviceType eq 'itemorder') {
3419     #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
3420     #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
3421     #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
3422     #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
3423     #}
3424
3425     if ( $serviceOptions->{record} ) {
3426         $Zpackage->option( record => $serviceOptions->{record} );
3427
3428         # can be xml or marc
3429         if ( $serviceOptions->{'syntax'} ) {
3430             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
3431         }
3432     }
3433
3434     # send the request, handle any exception encountered
3435     eval { $Zpackage->send($serviceType) };
3436     if ( $@ && $@->isa("ZOOM::Exception") ) {
3437         return "error:  " . $@->code() . " " . $@->message() . "\n";
3438     }
3439
3440     # free up package resources
3441     $Zpackage->destroy();
3442 }
3443
3444 =head2 set_service_options
3445
3446   my $serviceOptions = set_service_options($serviceType);
3447
3448 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
3449
3450 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
3451
3452 =cut
3453
3454 sub set_service_options {
3455     my ($serviceType) = @_;
3456     my $serviceOptions;
3457
3458     # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
3459     #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
3460
3461     if ( $serviceType eq 'commit' ) {
3462
3463         # nothing to do
3464     }
3465     if ( $serviceType eq 'create' ) {
3466
3467         # nothing to do
3468     }
3469     if ( $serviceType eq 'drop' ) {
3470         die "ERROR: 'drop' not currently supported (by Zebra)";
3471     }
3472     return $serviceOptions;
3473 }
3474
3475 =head2 get_biblio_authorised_values
3476
3477 find the types and values for all authorised values assigned to this biblio.
3478
3479 parameters:
3480     biblionumber
3481     MARC::Record of the bib
3482
3483 returns: a hashref mapping the authorised value to the value set for this biblionumber
3484
3485   $authorised_values = {
3486                        'Scent'     => 'flowery',
3487                        'Audience'  => 'Young Adult',
3488                        'itemtypes' => 'SER',
3489                         };
3490
3491 Notes: forlibrarian should probably be passed in, and called something different.
3492
3493 =cut
3494
3495 sub get_biblio_authorised_values {
3496     my $biblionumber = shift;
3497     my $record       = shift;
3498
3499     my $forlibrarian  = 1;                                 # are we in staff or opac?
3500     my $frameworkcode = GetFrameworkCode($biblionumber);
3501
3502     my $authorised_values;
3503
3504     my $tagslib = GetMarcStructure( $forlibrarian, $frameworkcode )
3505       or return $authorised_values;
3506
3507     # assume that these entries in the authorised_value table are bibliolevel.
3508     # ones that start with 'item%' are item level.
3509     my $query = q(SELECT distinct authorised_value, kohafield
3510                     FROM marc_subfield_structure
3511                     WHERE authorised_value !=''
3512                       AND (kohafield like 'biblio%'
3513                        OR  kohafield like '') );
3514     my $bibliolevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
3515
3516     foreach my $tag ( keys(%$tagslib) ) {
3517         foreach my $subfield ( keys( %{ $tagslib->{$tag} } ) ) {
3518
3519             # warn "checking $subfield. type is: " . ref $tagslib->{ $tag }{ $subfield };
3520             if ( 'HASH' eq ref $tagslib->{$tag}{$subfield} ) {
3521                 if ( defined $tagslib->{$tag}{$subfield}{'authorised_value'} && exists $bibliolevel_authorised_values->{ $tagslib->{$tag}{$subfield}{'authorised_value'} } ) {
3522                     if ( defined $record->field($tag) ) {
3523                         my $this_subfield_value = $record->field($tag)->subfield($subfield);
3524                         if ( defined $this_subfield_value ) {
3525                             $authorised_values->{ $tagslib->{$tag}{$subfield}{'authorised_value'} } = $this_subfield_value;
3526                         }
3527                     }
3528                 }
3529             }
3530         }
3531     }
3532
3533     # warn ( Data::Dumper->Dump( [ $authorised_values ], [ 'authorised_values' ] ) );
3534     return $authorised_values;
3535 }
3536
3537 1;
3538
3539 __END__
3540
3541 =head1 AUTHOR
3542
3543 Koha Development Team <http://koha-community.org/>
3544
3545 Paul POULAIN paul.poulain@free.fr
3546
3547 Joshua Ferraro jmf@liblime.com
3548
3549 =cut