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