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