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