dealing with empty XML : return an empty string, and not an empty MARC::Record
[koha_ffzg] / 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
22 require Exporter;
23 # use utf8;
24 use C4::Context;
25 use MARC::Record;
26 use MARC::File::USMARC;
27 use MARC::File::XML;
28 use ZOOM;
29 use C4::Koha;
30 use C4::Dates qw/format_date/;
31 use C4::Log; # logaction
32 use C4::ClassSource;
33
34 use vars qw($VERSION @ISA @EXPORT);
35
36 # TODO: fix version
37 # $VERSION = ?;
38
39 @ISA = qw( Exporter );
40
41 # EXPORTED FUNCTIONS.
42
43 # to add biblios or items
44 push @EXPORT, qw( &AddBiblio &AddItem );
45
46 # to get something
47 push @EXPORT, qw(
48   &GetBiblio
49   &GetBiblioData
50   &GetBiblioItemData
51   &GetBiblioItemInfosOf
52   &GetBiblioItemByBiblioNumber
53   &GetBiblioFromItemNumber
54   
55   &GetMarcItem
56   &GetItem
57   &GetItemInfosOf
58   &GetItemStatus
59   &GetItemLocation
60   &GetLostItems
61   &GetItemsForInventory
62   &GetItemsCount
63
64   &GetMarcNotes
65   &GetMarcSubjects
66   &GetMarcBiblio
67   &GetMarcAuthors
68   &GetMarcSeries
69   GetMarcUrls
70   &GetUsedMarcStructure
71
72   &GetItemsInfo
73   &GetItemsByBiblioitemnumber
74   &GetItemnumberFromBarcode
75   &get_itemnumbers_of
76   &GetXmlBiblio
77
78   &GetAuthorisedValueDesc
79   &GetMarcStructure
80   &GetMarcFromKohaField
81   &GetFrameworkCode
82   &GetPublisherNameFromIsbn
83   &TransformKohaToMarc
84 );
85
86 # To modify something
87 push @EXPORT, qw(
88   &ModBiblio
89   &ModItem
90   &ModItemTransfer
91   &ModBiblioframework
92   &ModZebra
93   &ModItemInMarc
94   &ModItemInMarconefield
95   &ModDateLastSeen
96 );
97
98 # To delete something
99 push @EXPORT, qw(
100   &DelBiblio
101   &DelItem
102 );
103
104 # Internal functions
105 # those functions are exported but should not be used
106 # they are usefull is few circumstances, so are exported.
107 # but don't use them unless you're a core developer ;-)
108 push @EXPORT, qw(
109   &ModBiblioMarc
110   &AddItemInMarc
111 );
112
113 # Others functions
114 push @EXPORT, qw(
115   &TransformMarcToKoha
116   &TransformHtmlToMarc2
117   &TransformHtmlToMarc
118   &TransformHtmlToXml
119   &PrepareItemrecordDisplay
120   &char_decode
121   &GetNoZebraIndexes
122 );
123
124 =head1 NAME
125
126 C4::Biblio - cataloging management functions
127
128 =head1 DESCRIPTION
129
130 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:
131
132 =over 4
133
134 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
135
136 =item 2. as raw MARC in the Zebra index and storage engine
137
138 =item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
139
140 =back
141
142 In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
143
144 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.
145
146 =over 4
147
148 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
149
150 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
151
152 =back
153
154 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:
155
156 =over 4
157
158 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
159
160 =item 2. _koha_* - low-level internal functions for managing the koha tables
161
162 =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.
163
164 =item 4. Zebra functions used to update the Zebra index
165
166 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
167
168 =back
169
170 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 :
171
172 =over 4
173
174 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
175
176 =item 2. add the biblionumber and biblioitemnumber into the MARC records
177
178 =item 3. save the marc record
179
180 =back
181
182 When dealing with items, we must :
183
184 =over 4
185
186 =item 1. save the item in items table, that gives us an itemnumber
187
188 =item 2. add the itemnumber to the item MARC field
189
190 =item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
191
192 When modifying a biblio or an item, the behaviour is quite similar.
193
194 =back
195
196 =head1 EXPORTED FUNCTIONS
197
198 =head2 AddBiblio
199
200 =over 4
201
202 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
203 Exported function (core API) for adding a new biblio to koha.
204
205 =back
206
207 =cut
208
209 sub AddBiblio {
210     my ( $record, $frameworkcode ) = @_;
211         my ($biblionumber,$biblioitemnumber,$error);
212     my $dbh = C4::Context->dbh;
213     # transform the data into koha-table style data
214     my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
215     ($biblionumber,$error) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
216     $olddata->{'biblionumber'} = $biblionumber;
217     ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $olddata );
218
219     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
220
221     # now add the record
222     $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
223       
224     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio") 
225         if C4::Context->preference("CataloguingLog");
226
227     return ( $biblionumber, $biblioitemnumber );
228 }
229
230 =head2 AddItem
231
232 =over 2
233
234     $biblionumber = AddItem( $record, $biblionumber)
235     Exported function (core API) for adding a new item to Koha
236
237 =back
238
239 =cut
240
241 sub AddItem {
242     my ( $record, $biblionumber ) = @_;
243     my $dbh = C4::Context->dbh;
244     
245     # add item in old-DB
246     my $frameworkcode = GetFrameworkCode( $biblionumber );
247     my $item = &TransformMarcToKoha( $dbh, $record, $frameworkcode );
248
249     # needs old biblionumber and biblioitemnumber
250     $item->{'biblionumber'} = $biblionumber;
251     my $sth =
252       $dbh->prepare(
253         "SELECT biblioitemnumber,itemtype FROM biblioitems WHERE biblionumber=?"
254       );
255     $sth->execute( $item->{'biblionumber'} );
256     my $itemtype;
257     ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow;
258     $sth =
259       $dbh->prepare(
260         "SELECT notforloan FROM itemtypes WHERE itemtype='$itemtype'");
261     $sth->execute();
262     my $notforloan = $sth->fetchrow;
263     ##Change the notforloan field if $notforloan found
264     if ( $notforloan > 0 ) {
265         $item->{'notforloan'} = $notforloan;
266         &MARCitemchange( $record, "items.notforloan", $notforloan );
267     }
268     if ( !$item->{'dateaccessioned'} || $item->{'dateaccessioned'} eq '' ) {
269
270         # find today's date
271         my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
272           localtime(time);
273         $year += 1900;
274         $mon  += 1;
275         my $date =
276           "$year-" . sprintf( "%0.2d", $mon ) . "-" . sprintf( "%0.2d", $mday );
277         $item->{'dateaccessioned'} = $date;
278         &MARCitemchange( $record, "items.dateaccessioned", $date );
279     }
280     my ( $itemnumber, $error ) = &_koha_new_items( $dbh, $item, $item->{barcode} );
281     # add itemnumber to MARC::Record before adding the item.
282     $sth = $dbh->prepare(
283 "SELECT tagfield,tagsubfield 
284 FROM marc_subfield_structure
285 WHERE frameworkcode=? 
286         AND kohafield=?"
287       );
288     &TransformKohaToMarcOneField( $sth, $record, "items.itemnumber", $itemnumber,
289         $frameworkcode );
290
291     # add the item
292     &AddItemInMarc( $record, $item->{'biblionumber'},$frameworkcode );
293    
294     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") 
295         if C4::Context->preference("CataloguingLog");
296     
297     return ($item->{biblionumber}, $item->{biblioitemnumber},$itemnumber);
298 }
299
300 =head2 ModBiblio
301
302     ModBiblio( $record,$biblionumber,$frameworkcode);
303     Exported function (core API) to modify a biblio
304
305 =cut
306
307 sub ModBiblio {
308     my ( $record, $biblionumber, $frameworkcode ) = @_;
309     if (C4::Context->preference("CataloguingLog")) {
310         my $newrecord = GetMarcBiblio($biblionumber);
311         &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,"BEFORE=>".$newrecord->as_formatted);
312     }
313     
314     my $dbh = C4::Context->dbh;
315     
316     $frameworkcode = "" unless $frameworkcode;
317
318     # get the items before and append them to the biblio before updating the record, atm we just have the biblio
319     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
320     my $oldRecord = GetMarcBiblio( $biblionumber );
321     
322     # parse each item, and, for an unknown reason, re-encode each subfield 
323     # if you don't do that, the record will have encoding mixed
324     # and the biblio will be re-encoded.
325     # strange, I (Paul P.) searched more than 1 day to understand what happends
326     # but could only solve the problem this way...
327    my @fields = $oldRecord->field( $itemtag );
328     foreach my $fielditem ( @fields ){
329         my $field;
330         foreach ($fielditem->subfields()) {
331             if ($field) {
332                 $field->add_subfields(Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
333             } else {
334                 $field = MARC::Field->new("$itemtag",'','',Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
335             }
336           }
337         $record->append_fields($field);
338     }
339     
340     # update biblionumber and biblioitemnumber in MARC
341     # FIXME - this is assuming a 1 to 1 relationship between
342     # biblios and biblioitems
343     my $sth =  $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
344     $sth->execute($biblionumber);
345     my ($biblioitemnumber) = $sth->fetchrow;
346     $sth->finish();
347     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
348
349     # update the MARC record (that now contains biblio and items) with the new record data
350     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
351     
352     # load the koha-table data object
353     my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
354
355     # modify the other koha tables
356     _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
357     _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
358     return 1;
359 }
360
361 =head2 ModItem
362
363 =over 2
364
365 Exported function (core API) for modifying an item in Koha.
366
367 =back
368
369 =cut
370
371 sub ModItem {
372     my ( $record, $biblionumber, $itemnumber, $delete, $new_item_hashref )
373       = @_;
374     
375     #logging
376     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$record->as_formatted) 
377         if C4::Context->preference("CataloguingLog");
378       
379     my $dbh = C4::Context->dbh;
380     
381     # if we have a MARC record, we're coming from cataloging and so
382     # we do the whole routine: update the MARC and zebra, then update the koha
383     # tables
384     if ($record) {
385         my $frameworkcode = GetFrameworkCode( $biblionumber );
386         ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
387         my $olditem       = TransformMarcToKoha( $dbh, $record, $frameworkcode,'items');
388         $olditem->{'biblionumber'} = $biblionumber;
389         my $sth =  $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
390         $sth->execute($biblionumber);
391         my ($biblioitemnumber) = $sth->fetchrow;
392         $sth->finish(); 
393         $olditem->{'biblioitemnumber'} = $biblioitemnumber;
394         _koha_modify_item( $dbh, $olditem );
395         return $biblionumber;
396     }
397
398     # otherwise, we're just looking to modify something quickly
399     # (like a status) so we just update the koha tables
400     elsif ($new_item_hashref) {
401         _koha_modify_item( $dbh, $new_item_hashref );
402     }
403 }
404
405 sub ModItemTransfer {
406     my ( $itemnumber, $frombranch, $tobranch ) = @_;
407     
408     my $dbh = C4::Context->dbh;
409     
410     #new entry in branchtransfers....
411     my $sth = $dbh->prepare(
412         "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
413         VALUES (?, ?, NOW(), ?)");
414     $sth->execute($itemnumber, $frombranch, $tobranch);
415     #update holdingbranch in items .....
416      $sth= $dbh->prepare(
417           "UPDATE items SET holdingbranch = ? WHERE items.itemnumber = ?");
418     $sth->execute($tobranch,$itemnumber);
419     &ModDateLastSeen($itemnumber);
420     $sth = $dbh->prepare(
421         "SELECT biblionumber FROM items WHERE itemnumber=?"
422       );
423     $sth->execute($itemnumber);
424     while ( my ( $biblionumber ) = $sth->fetchrow ) {
425         &ModItemInMarconefield( $biblionumber, $itemnumber,
426             'items.holdingbranch', $tobranch );
427     }
428     return;
429 }
430
431 =head2 ModBiblioframework
432
433     ModBiblioframework($biblionumber,$frameworkcode);
434     Exported function to modify a biblio framework
435
436 =cut
437
438 sub ModBiblioframework {
439     my ( $biblionumber, $frameworkcode ) = @_;
440     my $dbh = C4::Context->dbh;
441     my $sth = $dbh->prepare(
442         "UPDATE biblio SET frameworkcode=? WHERE biblionumber=?"
443     );
444     $sth->execute($frameworkcode, $biblionumber);
445     return 1;
446 }
447
448 =head2 ModItemInMarconefield
449
450 =over
451
452 modify only 1 field in a MARC item (mainly used for holdingbranch, but could also be used for status modif - moving a book to "lost" on a long overdu for example)
453 &ModItemInMarconefield( $biblionumber, $itemnumber, $itemfield, $newvalue )
454
455 =back
456
457 =cut
458
459 sub ModItemInMarconefield {
460     my ( $biblionumber, $itemnumber, $itemfield, $newvalue ) = @_;
461     my $dbh = C4::Context->dbh;
462     if ( !defined $newvalue ) {
463         $newvalue = "";
464     }
465
466     my $record = GetMarcItem( $biblionumber, $itemnumber );
467     my ($tagfield, $tagsubfield) = GetMarcFromKohaField( $itemfield,'');
468     if ($tagfield && $tagsubfield) {
469         my $tag = $record->field($tagfield);
470         if ($tag) {
471 #             my $tagsubs = $record->field($tagfield)->subfield($tagsubfield);
472             $tag->update( $tagsubfield => $newvalue );
473             $record->delete_field($tag);
474             $record->insert_fields_ordered($tag);
475             &ModItemInMarc( $record, $biblionumber, $itemnumber, 0 );
476         }
477     }
478 }
479
480 =head2 ModItemInMarc
481
482 =over
483
484 &ModItemInMarc( $record, $biblionumber, $itemnumber )
485
486 =back
487
488 =cut
489
490 sub ModItemInMarc {
491     my ( $ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
492     my $dbh = C4::Context->dbh;
493     
494     # get complete MARC record & replace the item field by the new one
495     my $completeRecord = GetMarcBiblio($biblionumber);
496     my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
497     my $itemField = $ItemRecord->field($itemtag);
498     my @items = $completeRecord->field($itemtag);
499     foreach (@items) {
500         if ($_->subfield($itemsubfield) eq $itemnumber) {
501 #             $completeRecord->delete_field($_);
502             $_->replace_with($itemField);
503         }
504     }
505     # save the record
506     my $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
507     $sth->execute( $completeRecord->as_usmarc(), $completeRecord->as_xml_record(),$biblionumber );
508     $sth->finish;
509     ModZebra($biblionumber,"specialUpdate","biblioserver",$completeRecord);
510 }
511
512 =head2 ModDateLastSeen
513
514 &ModDateLastSeen($itemnum)
515 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking
516 C<$itemnum> is the item number
517
518 =cut
519
520 sub ModDateLastSeen {
521     my ($itemnum) = @_;
522     my $dbh       = C4::Context->dbh;
523     my $sth       =
524       $dbh->prepare(
525           "UPDATE items SET itemlost=0,datelastseen  = NOW() WHERE items.itemnumber = ?"
526       );
527     $sth->execute($itemnum);
528     return;
529 }
530 =head2 DelBiblio
531
532 =over
533
534 my $error = &DelBiblio($dbh,$biblionumber);
535 Exported function (core API) for deleting a biblio in koha.
536 Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)
537 Also backs it up to deleted* tables
538 Checks to make sure there are not issues on any of the items
539 return:
540 C<$error> : undef unless an error occurs
541
542 =back
543
544 =cut
545
546 sub DelBiblio {
547     my ( $biblionumber ) = @_;
548     my $dbh = C4::Context->dbh;
549     my $error;    # for error handling
550         
551         # First make sure this biblio has no items attached
552         my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
553         $sth->execute($biblionumber);
554         if (my $itemnumber = $sth->fetchrow){
555                 # Fix this to use a status the template can understand
556                 $error .= "This Biblio has items attached, please delete them first before deleting this biblio ";
557         }
558
559     return $error if $error;
560
561     # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
562     # for at least 2 reasons :
563     # - we need to read the biblio if NoZebra is set (to remove it from the indexes
564     # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
565     #   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)
566     ModZebra($biblionumber, "delete_record", "biblioserver", undef);
567
568     # delete biblio from Koha tables and save in deletedbiblio
569     $error = &_koha_delete_biblio( $dbh, $biblionumber );
570
571     # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
572     $sth =
573       $dbh->prepare(
574         "SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
575     $sth->execute($biblionumber);
576     while ( my $biblioitemnumber = $sth->fetchrow ) {
577
578         # delete this biblioitem
579         $error = &_koha_delete_biblioitems( $dbh, $biblioitemnumber );
580         return $error if $error;
581     }
582     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$biblionumber,"") 
583         if C4::Context->preference("CataloguingLog");
584     return;
585 }
586
587 =head2 DelItem
588
589 =over
590
591 DelItem( $biblionumber, $itemnumber );
592 Exported function (core API) for deleting an item record in Koha.
593
594 =back
595
596 =cut
597
598 sub DelItem {
599     my ( $dbh, $biblionumber, $itemnumber ) = @_;
600         
601         # check the item has no current issues
602         
603         
604     &_koha_delete_item( $dbh, $itemnumber );
605
606     # get the MARC record
607     my $record = GetMarcBiblio($biblionumber);
608     my $frameworkcode = GetFrameworkCode($biblionumber);
609
610     # backup the record
611     my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
612     $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
613
614     #search item field code
615     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
616     my @fields = $record->field($itemtag);
617
618     # delete the item specified
619     foreach my $field (@fields) {
620         if ( $field->subfield($itemsubfield) eq $itemnumber ) {
621             $record->delete_field($field);
622         }
623     }
624     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
625     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$itemnumber,"item") 
626         if C4::Context->preference("CataloguingLog");
627 }
628
629 =head2 GetBiblioData
630
631 =over 4
632
633 $data = &GetBiblioData($biblionumber);
634 Returns information about the book with the given biblionumber.
635 C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
636 the C<biblio> and C<biblioitems> tables in the
637 Koha database.
638 In addition, C<$data-E<gt>{subject}> is the list of the book's
639 subjects, separated by C<" , "> (space, comma, space).
640 If there are multiple biblioitems with the given biblionumber, only
641 the first one is considered.
642
643 =back
644
645 =cut
646
647 sub GetBiblioData {
648     my ( $bibnum ) = @_;
649     my $dbh = C4::Context->dbh;
650
651     my $query = "
652         SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
653         FROM biblio
654             LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
655             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
656         WHERE biblio.biblionumber = ?
657             AND biblioitems.biblionumber = biblio.biblionumber
658     ";
659     my $sth = $dbh->prepare($query);
660     $sth->execute($bibnum);
661     my $data;
662     $data = $sth->fetchrow_hashref;
663     $sth->finish;
664
665     return ($data);
666 }    # sub GetBiblioData
667
668
669 =head2 GetItemsInfo
670
671 =over 4
672
673   @results = &GetItemsInfo($biblionumber, $type);
674
675 Returns information about books with the given biblionumber.
676
677 C<$type> may be either C<intra> or anything else. If it is not set to
678 C<intra>, then the search will exclude lost, very overdue, and
679 withdrawn items.
680
681 C<&GetItemsInfo> returns a list of references-to-hash. Each element
682 contains a number of keys. Most of them are table items from the
683 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
684 Koha database. Other keys include:
685
686 =over 4
687
688 =item C<$data-E<gt>{branchname}>
689
690 The name (not the code) of the branch to which the book belongs.
691
692 =item C<$data-E<gt>{datelastseen}>
693
694 This is simply C<items.datelastseen>, except that while the date is
695 stored in YYYY-MM-DD format in the database, here it is converted to
696 DD/MM/YYYY format. A NULL date is returned as C<//>.
697
698 =item C<$data-E<gt>{datedue}>
699
700 =item C<$data-E<gt>{class}>
701
702 This is the concatenation of C<biblioitems.classification>, the book's
703 Dewey code, and C<biblioitems.subclass>.
704
705 =item C<$data-E<gt>{ocount}>
706
707 I think this is the number of copies of the book available.
708
709 =item C<$data-E<gt>{order}>
710
711 If this is set, it is set to C<One Order>.
712
713 =back
714
715 =back
716
717 =cut
718
719 sub GetItemsInfo {
720     my ( $biblionumber, $type ) = @_;
721     my $dbh   = C4::Context->dbh;
722     my $query = "SELECT *,items.notforloan as itemnotforloan
723                  FROM items 
724                  LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
725                  LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
726                  LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype
727                 WHERE items.biblionumber = ?
728                 ORDER BY items.dateaccessioned desc
729                  ";
730     my $sth = $dbh->prepare($query);
731     $sth->execute($biblionumber);
732     my $i = 0;
733     my @results;
734     my ( $date_due, $count_reserves );
735
736     while ( my $data = $sth->fetchrow_hashref ) {
737         my $datedue = '';
738         my $isth    = $dbh->prepare(
739             "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname
740             FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
741             WHERE  itemnumber = ?
742                 AND returndate IS NULL"
743         );
744         $isth->execute( $data->{'itemnumber'} );
745         if ( my $idata = $isth->fetchrow_hashref ) {
746             $data->{borrowernumber} = $idata->{borrowernumber};
747             $data->{cardnumber}     = $idata->{cardnumber};
748             $data->{surname}     = $idata->{surname};
749             $data->{firstname}     = $idata->{firstname};
750             $datedue                = format_date( $idata->{'date_due'} );
751         }
752         if ( $datedue eq '' ) {
753             #$datedue="Available";
754             my ( $restype, $reserves ) =
755               C4::Reserves::CheckReserves( $data->{'itemnumber'} );
756             if ($restype) {
757
758                 #$datedue=$restype;
759                 $count_reserves = $restype;
760             }
761         }
762         $isth->finish;
763
764         #get branch information.....
765         my $bsth = $dbh->prepare(
766             "SELECT * FROM branches WHERE branchcode = ?
767         "
768         );
769         $bsth->execute( $data->{'holdingbranch'} );
770         if ( my $bdata = $bsth->fetchrow_hashref ) {
771             $data->{'branchname'} = $bdata->{'branchname'};
772         }
773         my $date = format_date( $data->{'datelastseen'} );
774         $data->{'datelastseen'}   = $date;
775         $data->{'datedue'}        = $datedue;
776         $data->{'count_reserves'} = $count_reserves;
777
778         # get notforloan complete status if applicable
779         my $sthnflstatus = $dbh->prepare(
780             'SELECT authorised_value
781             FROM   marc_subfield_structure
782             WHERE  kohafield="items.notforloan"
783         '
784         );
785
786         $sthnflstatus->execute;
787         my ($authorised_valuecode) = $sthnflstatus->fetchrow;
788         if ($authorised_valuecode) {
789             $sthnflstatus = $dbh->prepare(
790                 "SELECT lib FROM authorised_values
791                  WHERE  category=?
792                  AND authorised_value=?"
793             );
794             $sthnflstatus->execute( $authorised_valuecode,
795                 $data->{itemnotforloan} );
796             my ($lib) = $sthnflstatus->fetchrow;
797             $data->{notforloan} = $lib;
798         }
799
800         # my stack procedures
801         my $stackstatus = $dbh->prepare(
802             'SELECT authorised_value
803              FROM   marc_subfield_structure
804              WHERE  kohafield="items.stack"
805         '
806         );
807         $stackstatus->execute;
808
809         ($authorised_valuecode) = $stackstatus->fetchrow;
810         if ($authorised_valuecode) {
811             $stackstatus = $dbh->prepare(
812                 "SELECT lib
813                  FROM   authorised_values
814                  WHERE  category=?
815                  AND    authorised_value=?
816             "
817             );
818             $stackstatus->execute( $authorised_valuecode, $data->{stack} );
819             my ($lib) = $stackstatus->fetchrow;
820             $data->{stack} = $lib;
821         }
822         $results[$i] = $data;
823         $i++;
824     }
825     $sth->finish;
826
827     return (@results);
828 }
829
830 =head2 getitemstatus
831
832 =over 4
833
834 $itemstatushash = &getitemstatus($fwkcode);
835 returns information about status.
836 Can be MARC dependant.
837 fwkcode is optional.
838 But basically could be can be loan or not
839 Create a status selector with the following code
840
841 =head3 in PERL SCRIPT
842
843 my $itemstatushash = getitemstatus;
844 my @itemstatusloop;
845 foreach my $thisstatus (keys %$itemstatushash) {
846     my %row =(value => $thisstatus,
847                 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
848             );
849     push @itemstatusloop, \%row;
850 }
851 $template->param(statusloop=>\@itemstatusloop);
852
853
854 =head3 in TEMPLATE
855
856             <select name="statusloop">
857                 <option value="">Default</option>
858             <!-- TMPL_LOOP name="statusloop" -->
859                 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
860             <!-- /TMPL_LOOP -->
861             </select>
862
863 =cut
864
865 sub GetItemStatus {
866
867     # returns a reference to a hash of references to status...
868     my ($fwk) = @_;
869     my %itemstatus;
870     my $dbh = C4::Context->dbh;
871     my $sth;
872     $fwk = '' unless ($fwk);
873     my ( $tag, $subfield ) =
874       GetMarcFromKohaField( "items.notforloan", $fwk );
875     if ( $tag and $subfield ) {
876         my $sth =
877           $dbh->prepare(
878                         "SELECT authorised_value
879                         FROM marc_subfield_structure
880                         WHERE tagfield=?
881                                 AND tagsubfield=?
882                                 AND frameworkcode=?
883                         "
884           );
885         $sth->execute( $tag, $subfield, $fwk );
886         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
887             my $authvalsth =
888               $dbh->prepare(
889                                 "SELECT authorised_value,lib
890                                 FROM authorised_values 
891                                 WHERE category=? 
892                                 ORDER BY lib
893                                 "
894               );
895             $authvalsth->execute($authorisedvaluecat);
896             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
897                 $itemstatus{$authorisedvalue} = $lib;
898             }
899             $authvalsth->finish;
900             return \%itemstatus;
901             exit 1;
902         }
903         else {
904
905             #No authvalue list
906             # build default
907         }
908         $sth->finish;
909     }
910
911     #No authvalue list
912     #build default
913     $itemstatus{"1"} = "Not For Loan";
914     return \%itemstatus;
915 }
916
917 =head2 getitemlocation
918
919 =over 4
920
921 $itemlochash = &getitemlocation($fwk);
922 returns informations about location.
923 where fwk stands for an optional framework code.
924 Create a location selector with the following code
925
926 =head3 in PERL SCRIPT
927
928 my $itemlochash = getitemlocation;
929 my @itemlocloop;
930 foreach my $thisloc (keys %$itemlochash) {
931     my $selected = 1 if $thisbranch eq $branch;
932     my %row =(locval => $thisloc,
933                 selected => $selected,
934                 locname => $itemlochash->{$thisloc},
935             );
936     push @itemlocloop, \%row;
937 }
938 $template->param(itemlocationloop => \@itemlocloop);
939
940 =head3 in TEMPLATE
941
942 <select name="location">
943     <option value="">Default</option>
944 <!-- TMPL_LOOP name="itemlocationloop" -->
945     <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
946 <!-- /TMPL_LOOP -->
947 </select>
948
949 =back
950
951 =cut
952
953 sub GetItemLocation {
954
955     # returns a reference to a hash of references to location...
956     my ($fwk) = @_;
957     my %itemlocation;
958     my $dbh = C4::Context->dbh;
959     my $sth;
960     $fwk = '' unless ($fwk);
961     my ( $tag, $subfield ) =
962       GetMarcFromKohaField( "items.location", $fwk );
963     if ( $tag and $subfield ) {
964         my $sth =
965           $dbh->prepare(
966                         "SELECT authorised_value
967                         FROM marc_subfield_structure 
968                         WHERE tagfield=? 
969                                 AND tagsubfield=? 
970                                 AND frameworkcode=?"
971           );
972         $sth->execute( $tag, $subfield, $fwk );
973         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
974             my $authvalsth =
975               $dbh->prepare(
976                                 "SELECT authorised_value,lib
977                                 FROM authorised_values
978                                 WHERE category=?
979                                 ORDER BY lib"
980               );
981             $authvalsth->execute($authorisedvaluecat);
982             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
983                 $itemlocation{$authorisedvalue} = $lib;
984             }
985             $authvalsth->finish;
986             return \%itemlocation;
987             exit 1;
988         }
989         else {
990
991             #No authvalue list
992             # build default
993         }
994         $sth->finish;
995     }
996
997     #No authvalue list
998     #build default
999     $itemlocation{"1"} = "Not For Loan";
1000     return \%itemlocation;
1001 }
1002
1003 =head2 GetLostItems
1004
1005 $items = GetLostItems($where,$orderby);
1006
1007 This function get the items lost into C<$items>.
1008
1009 =over 2
1010
1011 =item input:
1012 C<$where> is a hashref. it containts a field of the items table as key
1013 and the value to match as value.
1014 C<$orderby> is a field of the items table.
1015
1016 =item return:
1017 C<$items> is a reference to an array full of hasref which keys are items' table column.
1018
1019 =item usage in the perl script:
1020
1021 my %where;
1022 $where{barcode} = 0001548;
1023 my $items = GetLostItems( \%where, "homebranch" );
1024 $template->param(itemsloop => $items);
1025
1026 =back
1027
1028 =cut
1029
1030 sub GetLostItems {
1031     # Getting input args.
1032     my $where   = shift;
1033     my $orderby = shift;
1034     my $dbh     = C4::Context->dbh;
1035
1036     my $query   = "
1037         SELECT *
1038         FROM   items
1039         WHERE  itemlost IS NOT NULL
1040           AND  itemlost <> 0
1041     ";
1042     foreach my $key (keys %$where) {
1043         $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
1044     }
1045     $query .= " ORDER BY ".$orderby if defined $orderby;
1046
1047     my $sth = $dbh->prepare($query);
1048     $sth->execute;
1049     my @items;
1050     while ( my $row = $sth->fetchrow_hashref ){
1051         push @items, $row;
1052     }
1053     return \@items;
1054 }
1055
1056 =head2 GetItemsForInventory
1057
1058 $itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
1059
1060 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1061
1062 The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
1063 It is ordered by callnumber,title.
1064
1065 The minlocation & maxlocation parameters are used to specify a range of item callnumbers
1066 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1067 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1068
1069 =cut
1070
1071 sub GetItemsForInventory {
1072     my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
1073     my $dbh = C4::Context->dbh;
1074     my $sth;
1075     if ($datelastseen) {
1076         $datelastseen=format_date_in_iso($datelastseen);  
1077         my $query =
1078                 "SELECT itemnumber,barcode,itemcallnumber,title,author,biblio.biblionumber,datelastseen
1079                  FROM items
1080                    LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
1081                  WHERE itemcallnumber>= ?
1082                    AND itemcallnumber <=?
1083                    AND (datelastseen< ? OR datelastseen IS NULL)";
1084         $query.= " AND items.location=".$dbh->quote($location) if $location;
1085         $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1086         $query .= " ORDER BY itemcallnumber,title";
1087         $sth = $dbh->prepare($query);
1088         $sth->execute( $minlocation, $maxlocation, $datelastseen );
1089     }
1090     else {
1091         my $query ="
1092                 SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen
1093                 FROM items 
1094                   LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
1095                 WHERE itemcallnumber>= ?
1096                   AND itemcallnumber <=?";
1097         $query.= " AND items.location=".$dbh->quote($location) if $location;
1098         $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1099         $query .= " ORDER BY itemcallnumber,title";
1100         $sth = $dbh->prepare($query);
1101         $sth->execute( $minlocation, $maxlocation );
1102     }
1103     my @results;
1104     while ( my $row = $sth->fetchrow_hashref ) {
1105         $offset-- if ($offset);
1106         $row->{datelastseen}=format_date($row->{datelastseen});
1107         if ( ( !$offset ) && $size ) {
1108             push @results, $row;
1109             $size--;
1110         }
1111     }
1112     return \@results;
1113 }
1114
1115 =head2 &GetBiblioItemData
1116
1117 =over 4
1118
1119 $itemdata = &GetBiblioItemData($biblioitemnumber);
1120
1121 Looks up the biblioitem with the given biblioitemnumber. Returns a
1122 reference-to-hash. The keys are the fields from the C<biblio>,
1123 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
1124 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
1125
1126 =back
1127
1128 =cut
1129
1130 #'
1131 sub GetBiblioItemData {
1132     my ($biblioitemnumber) = @_;
1133     my $dbh       = C4::Context->dbh;
1134     my $sth       =
1135       $dbh->prepare(
1136         "SELECT *,biblioitems.notes AS bnotes
1137                 FROM biblioitems,biblio,itemtypes 
1138         WHERE biblio.biblionumber = biblioitems.biblionumber 
1139                 AND biblioitemnumber = ? "
1140       );
1141     my $data;
1142     $sth->execute($biblioitemnumber);
1143     $data = $sth->fetchrow_hashref;
1144     $sth->finish;
1145     return ($data);
1146 }    # sub &GetBiblioItemData
1147
1148 =head2 GetItemnumberFromBarcode
1149
1150 =over 4
1151
1152 $result = GetItemnumberFromBarcode($barcode);
1153
1154 =back
1155
1156 =cut
1157
1158 sub GetItemnumberFromBarcode {
1159     my ($barcode) = @_;
1160     my $dbh = C4::Context->dbh;
1161
1162     my $rq =
1163       $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1164     $rq->execute($barcode);
1165     my ($result) = $rq->fetchrow;
1166     return ($result);
1167 }
1168
1169 =head2 GetBiblioItemByBiblioNumber
1170
1171 =over 4
1172
1173 NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.
1174
1175 =back
1176
1177 =cut
1178
1179 sub GetBiblioItemByBiblioNumber {
1180     my ($biblionumber) = @_;
1181     my $dbh = C4::Context->dbh;
1182     my $sth = $dbh->prepare("Select * FROM biblioitems WHERE biblionumber = ?");
1183     my $count = 0;
1184     my @results;
1185
1186     $sth->execute($biblionumber);
1187
1188     while ( my $data = $sth->fetchrow_hashref ) {
1189         push @results, $data;
1190     }
1191
1192     $sth->finish;
1193     return @results;
1194 }
1195
1196 =head2 GetBiblioFromItemNumber
1197
1198 =over 4
1199
1200 $item = &GetBiblioFromItemNumber($itemnumber);
1201
1202 Looks up the item with the given itemnumber.
1203
1204 C<&itemnodata> returns a reference-to-hash whose keys are the fields
1205 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
1206 database.
1207
1208 =back
1209
1210 =cut
1211
1212 #'
1213 sub GetBiblioFromItemNumber {
1214     my ( $itemnumber ) = @_;
1215     my $dbh = C4::Context->dbh;
1216     my $sth = $dbh->prepare(
1217         "SELECT * FROM items 
1218         LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1219         LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1220          WHERE items.itemnumber = ?"
1221     );
1222
1223     $sth->execute($itemnumber);
1224     my $data = $sth->fetchrow_hashref;
1225     $sth->finish;
1226     return ($data);
1227 }
1228
1229 =head2 GetBiblio
1230
1231 =over 4
1232
1233 ( $count, @results ) = &GetBiblio($biblionumber);
1234
1235 =back
1236
1237 =cut
1238
1239 sub GetBiblio {
1240     my ($biblionumber) = @_;
1241     my $dbh = C4::Context->dbh;
1242     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber = ?");
1243     my $count = 0;
1244     my @results;
1245     $sth->execute($biblionumber);
1246     while ( my $data = $sth->fetchrow_hashref ) {
1247         $results[$count] = $data;
1248         $count++;
1249     }    # while
1250     $sth->finish;
1251     return ( $count, @results );
1252 }    # sub GetBiblio
1253
1254 =head2 GetItem
1255
1256 =over 4
1257
1258 $data = &GetItem($itemnumber,$barcode);
1259
1260 return Item information, for a given itemnumber or barcode
1261
1262 =back
1263
1264 =cut
1265
1266 sub GetItem {
1267     my ($itemnumber,$barcode) = @_;
1268     my $dbh = C4::Context->dbh;
1269     if ($itemnumber) {
1270         my $sth = $dbh->prepare("
1271             SELECT * FROM items 
1272             WHERE itemnumber = ?");
1273         $sth->execute($itemnumber);
1274         my $data = $sth->fetchrow_hashref;
1275         return $data;
1276     } else {
1277         my $sth = $dbh->prepare("
1278             SELECT * FROM items 
1279             WHERE barcode = ?"
1280             );
1281         $sth->execute($barcode);
1282         my $data = $sth->fetchrow_hashref;
1283         return $data;
1284     }
1285 }    # sub GetItem
1286
1287 =head2 get_itemnumbers_of
1288
1289 =over 4
1290
1291 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1292
1293 Given a list of biblionumbers, return the list of corresponding itemnumbers
1294 for each biblionumber.
1295
1296 Return a reference on a hash where keys are biblionumbers and values are
1297 references on array of itemnumbers.
1298
1299 =back
1300
1301 =cut
1302
1303 sub get_itemnumbers_of {
1304     my @biblionumbers = @_;
1305
1306     my $dbh = C4::Context->dbh;
1307
1308     my $query = '
1309         SELECT itemnumber,
1310             biblionumber
1311         FROM items
1312         WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1313     ';
1314     my $sth = $dbh->prepare($query);
1315     $sth->execute(@biblionumbers);
1316
1317     my %itemnumbers_of;
1318
1319     while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1320         push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1321     }
1322
1323     return \%itemnumbers_of;
1324 }
1325
1326 =head2 GetItemInfosOf
1327
1328 =over 4
1329
1330 GetItemInfosOf(@itemnumbers);
1331
1332 =back
1333
1334 =cut
1335
1336 sub GetItemInfosOf {
1337     my @itemnumbers = @_;
1338
1339     my $query = '
1340         SELECT *
1341         FROM items
1342         WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1343     ';
1344     return get_infos_of( $query, 'itemnumber' );
1345 }
1346
1347 =head2 GetItemsByBiblioitemnumber
1348
1349 =over 4
1350
1351 GetItemsByBiblioitemnumber($biblioitemnumber);
1352
1353 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1354 Called by moredetail.pl
1355
1356 =back
1357
1358 =cut
1359
1360 sub GetItemsByBiblioitemnumber {
1361         my ( $bibitem ) = @_;
1362         my $dbh = C4::Context->dbh;
1363         my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1364         # Get all items attached to a biblioitem
1365     my $i = 0;
1366     my @results; 
1367     $sth->execute($bibitem) || die $sth->errstr;
1368     while ( my $data = $sth->fetchrow_hashref ) {  
1369                 # Foreach item, get circulation information
1370                 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1371                                    WHERE itemnumber = ?
1372                                    AND returndate is NULL
1373                                    AND issues.borrowernumber = borrowers.borrowernumber"
1374         );
1375         $sth2->execute( $data->{'itemnumber'} );
1376         if ( my $data2 = $sth2->fetchrow_hashref ) {
1377                         # if item is out, set the due date and who it is out too
1378                         $data->{'date_due'}   = $data2->{'date_due'};
1379                         $data->{'cardnumber'} = $data2->{'cardnumber'};
1380                         $data->{'borrowernumber'}   = $data2->{'borrowernumber'};
1381                 }
1382         else {
1383                         # set date_due to blank, so in the template we check itemlost, and wthdrawn 
1384                         $data->{'date_due'} = '';                                                                                                         
1385                 }    # else         
1386         $sth2->finish;
1387         # Find the last 3 people who borrowed this item.                  
1388         my $query2 = "SELECT * FROM issues, borrowers WHERE itemnumber = ?
1389                       AND issues.borrowernumber = borrowers.borrowernumber
1390                       AND returndate is not NULL
1391                       ORDER BY returndate desc,timestamp desc LIMIT 3";
1392         $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1393         $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1394         my $i2 = 0;
1395         while ( my $data2 = $sth2->fetchrow_hashref ) {
1396                         $data->{"timestamp$i2"} = $data2->{'timestamp'};
1397                         $data->{"card$i2"}      = $data2->{'cardnumber'};
1398                         $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
1399                         $i2++;
1400                 }
1401         $sth2->finish;
1402         push(@results,$data);
1403     } 
1404     $sth->finish;
1405     return (\@results); 
1406 }
1407
1408
1409 =head2 GetBiblioItemInfosOf
1410
1411 =over 4
1412
1413 GetBiblioItemInfosOf(@biblioitemnumbers);
1414
1415 =back
1416
1417 =cut
1418
1419 sub GetBiblioItemInfosOf {
1420     my @biblioitemnumbers = @_;
1421
1422     my $query = '
1423         SELECT biblioitemnumber,
1424             publicationyear,
1425             itemtype
1426         FROM biblioitems
1427         WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
1428     ';
1429     return get_infos_of( $query, 'biblioitemnumber' );
1430 }
1431
1432 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
1433
1434 =head2 GetMarcStructure
1435
1436 =over 4
1437
1438 $res = GetMarcStructure($forlibrarian,$frameworkcode);
1439
1440 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
1441 $forlibrarian  :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
1442 $frameworkcode : the framework code to read
1443
1444 =back
1445
1446 =cut
1447
1448 sub GetMarcStructure {
1449     my ( $forlibrarian, $frameworkcode ) = @_;
1450     my $dbh=C4::Context->dbh;
1451     $frameworkcode = "" unless $frameworkcode;
1452     my $sth;
1453     my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';
1454
1455     # check that framework exists
1456     $sth =
1457       $dbh->prepare(
1458         "SELECT COUNT(*) FROM marc_tag_structure WHERE frameworkcode=?");
1459     $sth->execute($frameworkcode);
1460     my ($total) = $sth->fetchrow;
1461     $frameworkcode = "" unless ( $total > 0 );
1462     $sth =
1463       $dbh->prepare(
1464                 "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable 
1465                 FROM marc_tag_structure 
1466                 WHERE frameworkcode=? 
1467                 ORDER BY tagfield"
1468       );
1469     $sth->execute($frameworkcode);
1470     my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
1471
1472     while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) =
1473         $sth->fetchrow )
1474     {
1475         $res->{$tag}->{lib} =
1476           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1477         $res->{$tab}->{tab}        = "";
1478         $res->{$tag}->{mandatory}  = $mandatory;
1479         $res->{$tag}->{repeatable} = $repeatable;
1480     }
1481
1482     $sth =
1483       $dbh->prepare(
1484                         "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue 
1485                                 FROM marc_subfield_structure 
1486                         WHERE frameworkcode=? 
1487                                 ORDER BY tagfield,tagsubfield
1488                         "
1489     );
1490     
1491     $sth->execute($frameworkcode);
1492
1493     my $subfield;
1494     my $authorised_value;
1495     my $authtypecode;
1496     my $value_builder;
1497     my $kohafield;
1498     my $seealso;
1499     my $hidden;
1500     my $isurl;
1501     my $link;
1502     my $defaultvalue;
1503
1504     while (
1505         (
1506             $tag,          $subfield,      $liblibrarian,
1507             ,              $libopac,       $tab,
1508             $mandatory,    $repeatable,    $authorised_value,
1509             $authtypecode, $value_builder, $kohafield,
1510             $seealso,      $hidden,        $isurl,
1511             $link,$defaultvalue
1512         )
1513         = $sth->fetchrow
1514       )
1515     {
1516         $res->{$tag}->{$subfield}->{lib} =
1517           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1518         $res->{$tag}->{$subfield}->{tab}              = $tab;
1519         $res->{$tag}->{$subfield}->{mandatory}        = $mandatory;
1520         $res->{$tag}->{$subfield}->{repeatable}       = $repeatable;
1521         $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
1522         $res->{$tag}->{$subfield}->{authtypecode}     = $authtypecode;
1523         $res->{$tag}->{$subfield}->{value_builder}    = $value_builder;
1524         $res->{$tag}->{$subfield}->{kohafield}        = $kohafield;
1525         $res->{$tag}->{$subfield}->{seealso}          = $seealso;
1526         $res->{$tag}->{$subfield}->{hidden}           = $hidden;
1527         $res->{$tag}->{$subfield}->{isurl}            = $isurl;
1528         $res->{$tag}->{$subfield}->{'link'}           = $link;
1529         $res->{$tag}->{$subfield}->{defaultvalue}     = $defaultvalue;
1530     }
1531     return $res;
1532 }
1533
1534 =head2 GetUsedMarcStructure
1535
1536     the same function as GetMarcStructure expcet it just take field
1537     in tab 0-9. (used field)
1538     
1539     my $results = GetUsedMarcStructure($frameworkcode);
1540     
1541     L<$results> is a ref to an array which each case containts a ref
1542     to a hash which each keys is the columns from marc_subfield_structure
1543     
1544     L<$frameworkcode> is the framework code. 
1545     
1546 =cut
1547
1548 sub GetUsedMarcStructure($){
1549     my $frameworkcode = shift || '';
1550     my $dbh           = C4::Context->dbh;
1551     my $query         = qq/
1552         SELECT *
1553         FROM   marc_subfield_structure
1554         WHERE   tab > -1 
1555             AND frameworkcode = ?
1556     /;
1557     my @results;
1558     my $sth = $dbh->prepare($query);
1559     $sth->execute($frameworkcode);
1560     while (my $row = $sth->fetchrow_hashref){
1561         push @results,$row;
1562     }
1563     return \@results;
1564 }
1565
1566 =head2 GetMarcFromKohaField
1567
1568 =over 4
1569
1570 ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
1571 Returns the MARC fields & subfields mapped to the koha field 
1572 for the given frameworkcode
1573
1574 =back
1575
1576 =cut
1577
1578 sub GetMarcFromKohaField {
1579     my ( $kohafield, $frameworkcode ) = @_;
1580     return 0, 0 unless $kohafield;
1581     my $relations = C4::Context->marcfromkohafield;
1582     return (
1583         $relations->{$frameworkcode}->{$kohafield}->[0],
1584         $relations->{$frameworkcode}->{$kohafield}->[1]
1585     );
1586 }
1587
1588 =head2 GetMarcBiblio
1589
1590 =over 4
1591
1592 Returns MARC::Record of the biblionumber passed in parameter.
1593 the marc record contains both biblio & item datas
1594
1595 =back
1596
1597 =cut
1598
1599 sub GetMarcBiblio {
1600     my $biblionumber = shift;
1601     my $dbh          = C4::Context->dbh;
1602     my $sth          =
1603       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1604     $sth->execute($biblionumber);
1605      my ($marcxml) = $sth->fetchrow;
1606      MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
1607      $marcxml =~ s/\x1e//g;
1608      $marcxml =~ s/\x1f//g;
1609      $marcxml =~ s/\x1d//g;
1610      $marcxml =~ s/\x0f//g;
1611      $marcxml =~ s/\x0c//g;  
1612 #   warn $marcxml;
1613     my $record = MARC::Record->new();
1614     if ($marcxml) {
1615         $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
1616         if ($@) {warn $@;}
1617 #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
1618         return $record;
1619     } else {
1620         return undef;
1621     }
1622 }
1623
1624 =head2 GetXmlBiblio
1625
1626 =over 4
1627
1628 my $marcxml = GetXmlBiblio($biblionumber);
1629
1630 Returns biblioitems.marcxml of the biblionumber passed in parameter.
1631 The XML contains both biblio & item datas
1632
1633 =back
1634
1635 =cut
1636
1637 sub GetXmlBiblio {
1638     my ( $biblionumber ) = @_;
1639     my $dbh = C4::Context->dbh;
1640     my $sth =
1641       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1642     $sth->execute($biblionumber);
1643     my ($marcxml) = $sth->fetchrow;
1644     return $marcxml;
1645 }
1646
1647 =head2 GetAuthorisedValueDesc
1648
1649 =over 4
1650
1651 my $subfieldvalue =get_authorised_value_desc(
1652     $tag, $subf[$i][0],$subf[$i][1], '', $taglib);
1653 Retrieve the complete description for a given authorised value.
1654
1655 =back
1656
1657 =cut
1658
1659 sub GetAuthorisedValueDesc {
1660     my ( $tag, $subfield, $value, $framework, $tagslib ) = @_;
1661     my $dbh = C4::Context->dbh;
1662     
1663     #---- branch
1664     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1665         return C4::Branch::GetBranchName($value);
1666     }
1667
1668     #---- itemtypes
1669     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1670         return getitemtypeinfo($value)->{description};
1671     }
1672
1673     #---- "true" authorized value
1674     my $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1675     if ( $category ne "" ) {
1676         my $sth =
1677           $dbh->prepare(
1678             "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
1679           );
1680         $sth->execute( $category, $value );
1681         my $data = $sth->fetchrow_hashref;
1682         return $data->{'lib'};
1683     }
1684     else {
1685         return $value;    # if nothing is found return the original value
1686     }
1687 }
1688
1689 =head2 GetMarcItem
1690
1691 =over 4
1692
1693 Returns MARC::Record of the item passed in parameter.
1694
1695 =back
1696
1697 =cut
1698
1699 sub GetMarcItem {
1700     my ( $biblionumber, $itemnumber ) = @_;
1701     my $dbh = C4::Context->dbh;
1702     my $newrecord = MARC::Record->new();
1703     my $marcflavour = C4::Context->preference('marcflavour');
1704     
1705     my $marcxml = GetXmlBiblio($biblionumber);
1706     my $record = MARC::Record->new();
1707     $record = MARC::Record::new_from_xml( $marcxml, "utf8", $marcflavour );
1708     # now, find where the itemnumber is stored & extract only the item
1709     my ( $itemnumberfield, $itemnumbersubfield ) =
1710       GetMarcFromKohaField( 'items.itemnumber', '' );
1711     my @fields = $record->field($itemnumberfield);
1712     foreach my $field (@fields) {
1713         if ( $field->subfield($itemnumbersubfield) eq $itemnumber ) {
1714             $newrecord->insert_fields_ordered($field);
1715         }
1716     }
1717     return $newrecord;
1718 }
1719
1720
1721
1722 =head2 GetMarcNotes
1723
1724 =over 4
1725
1726 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1727 Get all notes from the MARC record and returns them in an array.
1728 The note are stored in differents places depending on MARC flavour
1729
1730 =back
1731
1732 =cut
1733
1734 sub GetMarcNotes {
1735     my ( $record, $marcflavour ) = @_;
1736     my $scope;
1737     if ( $marcflavour eq "MARC21" ) {
1738         $scope = '5..';
1739     }
1740     else {    # assume unimarc if not marc21
1741         $scope = '3..';
1742     }
1743     my @marcnotes;
1744     my $note = "";
1745     my $tag  = "";
1746     my $marcnote;
1747     foreach my $field ( $record->field($scope) ) {
1748         my $value = $field->as_string();
1749         if ( $note ne "" ) {
1750             $marcnote = { marcnote => $note, };
1751             push @marcnotes, $marcnote;
1752             $note = $value;
1753         }
1754         if ( $note ne $value ) {
1755             $note = $note . " " . $value;
1756         }
1757     }
1758
1759     if ( $note ) {
1760         $marcnote = { marcnote => $note };
1761         push @marcnotes, $marcnote;    #load last tag into array
1762     }
1763     return \@marcnotes;
1764 }    # end GetMarcNotes
1765
1766 =head2 GetMarcSubjects
1767
1768 =over 4
1769
1770 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1771 Get all subjects from the MARC record and returns them in an array.
1772 The subjects are stored in differents places depending on MARC flavour
1773
1774 =back
1775
1776 =cut
1777
1778 sub GetMarcSubjects {
1779     my ( $record, $marcflavour ) = @_;
1780     my ( $mintag, $maxtag );
1781     if ( $marcflavour eq "MARC21" ) {
1782         $mintag = "600";
1783         $maxtag = "699";
1784     }
1785     else {    # assume unimarc if not marc21
1786         $mintag = "600";
1787         $maxtag = "611";
1788     }
1789
1790     my @marcsubjcts;
1791
1792     foreach my $field ( $record->fields ) {
1793         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1794         my @subfields = $field->subfields();
1795         my $link = "su:";
1796         my $label;
1797         my $flag = 0;
1798         my $authoritysep=C4::Context->preference("authoritysep");
1799         for my $subject_subfield ( @subfields ) {
1800             if (
1801                 $marcflavour ne 'MARC21'
1802                 and (
1803                     ($subject_subfield->[0] eq '3') or
1804                     ($subject_subfield->[0] eq '4') or
1805                     ($subject_subfield->[0] eq '5')
1806                 )
1807             )
1808             {
1809                 next;
1810             }
1811             my $code = $subject_subfield->[0];
1812             $label .= $subject_subfield->[1].$authoritysep unless ( $code == 9 );
1813             $link  .= " and su-to:".$subject_subfield->[1]  unless ( $code == 9 );
1814             if ( $code == 9 ) {
1815                 $link = "an:".$subject_subfield->[1];
1816                 $flag = 1;
1817             }
1818             elsif ( ! $flag ) {
1819                 $link =~ s/ and\ssu-to:$//;
1820             }
1821         }
1822          $label =~ s/$authoritysep$//;
1823         push @marcsubjcts,
1824           {
1825             label => $label,
1826             link  => $link
1827           }
1828     }
1829     return \@marcsubjcts;
1830 }    #end GetMarcSubjects
1831
1832 =head2 GetMarcAuthors
1833
1834 =over 4
1835
1836 authors = GetMarcAuthors($record,$marcflavour);
1837 Get all authors from the MARC record and returns them in an array.
1838 The authors are stored in differents places depending on MARC flavour
1839
1840 =back
1841
1842 =cut
1843
1844 sub GetMarcAuthors {
1845     my ( $record, $marcflavour ) = @_;
1846     my ( $mintag, $maxtag );
1847     # tagslib useful for UNIMARC author reponsabilities
1848     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.
1849     if ( $marcflavour eq "MARC21" ) {
1850         $mintag = "700";
1851         $maxtag = "720"; 
1852     }
1853     elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
1854         $mintag = "701";
1855         $maxtag = "712";
1856     }
1857         else {
1858                 return;
1859         }
1860     my @marcauthors;
1861
1862     foreach my $field ( $record->fields ) {
1863         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1864         my %hash;
1865         my @subfields = $field->subfields();
1866         my $count_auth = 0;
1867         for my $authors_subfield (@subfields) {
1868                         #unimarc-specific line
1869             next if ($marcflavour eq 'UNIMARC' and (($authors_subfield->[0] eq '3') or ($authors_subfield->[0] eq '5')));
1870             my $subfieldcode = $authors_subfield->[0];
1871             my $value;
1872             # deal with UNIMARC author responsibility
1873                         if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq '4')) {
1874                 $value = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
1875             } else {
1876                 $value        = $authors_subfield->[1];
1877             }
1878             $hash{tag}       = $field->tag;
1879             $hash{value}    .= $value . " " if ($subfieldcode != 9) ;
1880             $hash{link}     .= $value if ($subfieldcode eq 9);
1881         }
1882         push @marcauthors, \%hash;
1883     }
1884     return \@marcauthors;
1885 }
1886
1887 =head2 GetMarcUrls
1888
1889 =over 4
1890
1891 $marcurls = GetMarcUrls($record,$marcflavour);
1892 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1893 Assumes web resources (not uncommon in MARC21 to omit resource type ind) 
1894
1895 =back
1896
1897 =cut
1898
1899 sub GetMarcUrls {
1900     my ($record, $marcflavour) = @_;
1901     my @marcurls;
1902     my $marcurl;
1903     for my $field ($record->field('856')) {
1904         my $url = $field->subfield('u');
1905         my @notes;
1906         for my $note ( $field->subfield('z')) {
1907             push @notes , {note => $note};
1908         }        
1909         $marcurl = {  MARCURL => $url,
1910                       notes => \@notes,
1911                                         };
1912                 if($marcflavour eq 'MARC21') {
1913                 my $s3 = $field->subfield('3');
1914                         my $link = $field->subfield('y');
1915             $marcurl->{'linktext'} = $link || $s3 || $url ;;
1916             $marcurl->{'part'} = $s3 if($link);
1917             $marcurl->{'toc'} = 1 if($s3 =~ /^[Tt]able/) ;
1918                 } else {
1919                         $marcurl->{'linktext'} = $url;
1920                 }
1921         push @marcurls, $marcurl;    
1922         }
1923     return \@marcurls;
1924 }  #end GetMarcUrls
1925
1926 =head2 GetMarcSeries
1927
1928 =over 4
1929
1930 $marcseriesarray = GetMarcSeries($record,$marcflavour);
1931 Get all series from the MARC record and returns them in an array.
1932 The series are stored in differents places depending on MARC flavour
1933
1934 =back
1935
1936 =cut
1937
1938 sub GetMarcSeries {
1939     my ($record, $marcflavour) = @_;
1940     my ($mintag, $maxtag);
1941     if ($marcflavour eq "MARC21") {
1942         $mintag = "440";
1943         $maxtag = "490";
1944     } else {           # assume unimarc if not marc21
1945         $mintag = "600";
1946         $maxtag = "619";
1947     }
1948
1949     my @marcseries;
1950     my $subjct = "";
1951     my $subfield = "";
1952     my $marcsubjct;
1953
1954     foreach my $field ($record->field('440'), $record->field('490')) {
1955         my @subfields_loop;
1956         #my $value = $field->subfield('a');
1957         #$marcsubjct = {MARCSUBJCT => $value,};
1958         my @subfields = $field->subfields();
1959         #warn "subfields:".join " ", @$subfields;
1960         my $counter = 0;
1961         my @link_loop;
1962         for my $series_subfield (@subfields) {
1963                         my $volume_number;
1964                         undef $volume_number;
1965                         # see if this is an instance of a volume
1966                         if ($series_subfield->[0] eq 'v') {
1967                                 $volume_number=1;
1968                         }
1969
1970             my $code = $series_subfield->[0];
1971             my $value = $series_subfield->[1];
1972             my $linkvalue = $value;
1973             $linkvalue =~ s/(\(|\))//g;
1974             my $operator = " and " unless $counter==0;
1975             push @link_loop, {link => $linkvalue, operator => $operator };
1976             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1977                         if ($volume_number) {
1978                         push @subfields_loop, {volumenum => $value};
1979                         }
1980                         else {
1981             push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number};
1982                         }
1983             $counter++;
1984         }
1985         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1986         #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1987         #push @marcsubjcts, $marcsubjct;
1988         #$subjct = $value;
1989
1990     }
1991     my $marcseriessarray=\@marcseries;
1992     return $marcseriessarray;
1993 }  #end getMARCseriess
1994
1995 =head2 GetFrameworkCode
1996
1997 =over 4
1998
1999     $frameworkcode = GetFrameworkCode( $biblionumber )
2000
2001 =back
2002
2003 =cut
2004
2005 sub GetFrameworkCode {
2006     my ( $biblionumber ) = @_;
2007     my $dbh = C4::Context->dbh;
2008     my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
2009     $sth->execute($biblionumber);
2010     my ($frameworkcode) = $sth->fetchrow;
2011     return $frameworkcode;
2012 }
2013
2014 =head2 GetPublisherNameFromIsbn
2015
2016     $name = GetPublishercodeFromIsbn($isbn);
2017     if(defined $name){
2018         ...
2019     }
2020
2021 =cut
2022
2023 sub GetPublisherNameFromIsbn($){
2024     my $isbn = shift;
2025     $isbn =~ s/[- _]//g;
2026     $isbn =~ s/^0*//;
2027     my @codes = (split '-', DisplayISBN($isbn));
2028     my $code = $codes[0].$codes[1].$codes[2];
2029     my $dbh  = C4::Context->dbh;
2030     my $query = qq{
2031         SELECT distinct publishercode
2032         FROM   biblioitems
2033         WHERE  isbn LIKE ?
2034         AND    publishercode IS NOT NULL
2035         LIMIT 1
2036     };
2037     my $sth = $dbh->prepare($query);
2038     $sth->execute("$code%");
2039     my $name = $sth->fetchrow;
2040     return $name if length $name;
2041     return undef;
2042 }
2043
2044 =head2 TransformKohaToMarc
2045
2046 =over 4
2047
2048     $record = TransformKohaToMarc( $hash )
2049     This function builds partial MARC::Record from a hash
2050     Hash entries can be from biblio or biblioitems.
2051     This function is called in acquisition module, to create a basic catalogue entry from user entry
2052
2053 =back
2054
2055 =cut
2056
2057 sub TransformKohaToMarc {
2058
2059     my ( $hash ) = @_;
2060     my $dbh = C4::Context->dbh;
2061     my $sth =
2062     $dbh->prepare(
2063         "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
2064     );
2065     my $record = MARC::Record->new();
2066     foreach (keys %{$hash}) {
2067         &TransformKohaToMarcOneField( $sth, $record, $_,
2068             $hash->{$_}, '' );
2069         }
2070     return $record;
2071 }
2072
2073 =head2 TransformKohaToMarcOneField
2074
2075 =over 4
2076
2077     $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
2078
2079 =back
2080
2081 =cut
2082
2083 sub TransformKohaToMarcOneField {
2084     my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
2085     $frameworkcode='' unless $frameworkcode;
2086     my $tagfield;
2087     my $tagsubfield;
2088
2089     if ( !defined $sth ) {
2090         my $dbh = C4::Context->dbh;
2091         $sth = $dbh->prepare(
2092             "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
2093         );
2094     }
2095     $sth->execute( $frameworkcode, $kohafieldname );
2096     if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
2097         my $tag = $record->field($tagfield);
2098         if ($tag) {
2099             $tag->update( $tagsubfield => $value );
2100             $record->delete_field($tag);
2101             $record->insert_fields_ordered($tag);
2102         }
2103         else {
2104             $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
2105         }
2106     }
2107     return $record;
2108 }
2109
2110 =head2 TransformHtmlToXml
2111
2112 =over 4
2113
2114 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type )
2115
2116 $auth_type contains :
2117 - nothing : rebuild a biblio, un UNIMARC the encoding is in 100$a pos 26/27
2118 - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
2119 - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
2120
2121 =back
2122
2123 =cut
2124
2125 sub TransformHtmlToXml {
2126     my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
2127     my $xml = MARC::File::XML::header('UTF-8');
2128     $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
2129     MARC::File::XML->default_record_format($auth_type);
2130     # in UNIMARC, field 100 contains the encoding
2131     # check that there is one, otherwise the 
2132     # MARC::Record->new_from_xml will fail (and Koha will die)
2133     my $unimarc_and_100_exist=0;
2134     $unimarc_and_100_exist=1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
2135     my $prevvalue;
2136     my $prevtag = -1;
2137     my $first   = 1;
2138     my $j       = -1;
2139     for ( my $i = 0 ; $i <= @$tags ; $i++ ) {
2140         if (C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a") {
2141             # if we have a 100 field and it's values are not correct, skip them.
2142             # if we don't have any valid 100 field, we will create a default one at the end
2143             my $enc = substr( @$values[$i], 26, 2 );
2144             if ($enc eq '01' or $enc eq '50' or $enc eq '03') {
2145                 $unimarc_and_100_exist=1;
2146             } else {
2147                 next;
2148             }
2149         }
2150         @$values[$i] =~ s/&/&amp;/g;
2151         @$values[$i] =~ s/</&lt;/g;
2152         @$values[$i] =~ s/>/&gt;/g;
2153         @$values[$i] =~ s/"/&quot;/g;
2154         @$values[$i] =~ s/'/&apos;/g;
2155 #         if ( !utf8::is_utf8( @$values[$i] ) ) {
2156 #             utf8::decode( @$values[$i] );
2157 #         }
2158         if ( ( @$tags[$i] ne $prevtag ) ) {
2159             $j++ unless ( @$tags[$i] eq "" );
2160             if ( !$first ) {
2161                 $xml .= "</datafield>\n";
2162                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
2163                     && ( @$values[$i] ne "" ) )
2164                 {
2165                     my $ind1 = substr( @$indicator[$j], 0, 1 );
2166                     my $ind2;
2167                     if ( @$indicator[$j] ) {
2168                         $ind2 = substr( @$indicator[$j], 1, 1 );
2169                     }
2170                     else {
2171                         warn "Indicator in @$tags[$i] is empty";
2172                         $ind2 = " ";
2173                     }
2174                     $xml .=
2175 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2176                     $xml .=
2177 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2178                     $first = 0;
2179                 }
2180                 else {
2181                     $first = 1;
2182                 }
2183             }
2184             else {
2185                 if ( @$values[$i] ne "" ) {
2186
2187                     # leader
2188                     if ( @$tags[$i] eq "000" ) {
2189                         $xml .= "<leader>@$values[$i]</leader>\n";
2190                         $first = 1;
2191
2192                         # rest of the fixed fields
2193                     }
2194                     elsif ( @$tags[$i] < 10 ) {
2195                         $xml .=
2196 "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2197                         $first = 1;
2198                     }
2199                     else {
2200                         my $ind1 = substr( @$indicator[$j], 0, 1 );
2201                         my $ind2 = substr( @$indicator[$j], 1, 1 );
2202                         $xml .=
2203 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2204                         $xml .=
2205 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2206                         $first = 0;
2207                     }
2208                 }
2209             }
2210         }
2211         else {    # @$tags[$i] eq $prevtag
2212             if ( @$values[$i] eq "" ) {
2213             }
2214             else {
2215                 if ($first) {
2216                     my $ind1 = substr( @$indicator[$j], 0, 1 );
2217                     my $ind2 = substr( @$indicator[$j], 1, 1 );
2218                     $xml .=
2219 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2220                     $first = 0;
2221                 }
2222                 $xml .=
2223 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2224             }
2225         }
2226         $prevtag = @$tags[$i];
2227     }
2228     if (C4::Context->preference('marcflavour') and !$unimarc_and_100_exist) {
2229 #     warn "SETTING 100 for $auth_type";
2230         use POSIX qw(strftime);
2231         my $string = strftime( "%Y%m%d", localtime(time) );
2232         # set 50 to position 26 is biblios, 13 if authorities
2233         my $pos=26;
2234         $pos=13 if $auth_type eq 'UNIMARCAUTH';
2235         $string = sprintf( "%-*s", 35, $string );
2236         substr( $string, $pos , 6, "50" );
2237         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2238         $xml .= "<subfield code=\"a\">$string</subfield>\n";
2239         $xml .= "</datafield>\n";
2240     }
2241     $xml .= MARC::File::XML::footer();
2242     return $xml;
2243 }
2244
2245 =head2 TransformHtmlToMarc
2246
2247     L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
2248     L<$params> is a ref to an array as below:
2249     {
2250         'tag_010_indicator_531951' ,
2251         'tag_010_code_a_531951_145735' ,
2252         'tag_010_subfield_a_531951_145735' ,
2253         'tag_200_indicator_873510' ,
2254         'tag_200_code_a_873510_673465' ,
2255         'tag_200_subfield_a_873510_673465' ,
2256         'tag_200_code_b_873510_704318' ,
2257         'tag_200_subfield_b_873510_704318' ,
2258         'tag_200_code_e_873510_280822' ,
2259         'tag_200_subfield_e_873510_280822' ,
2260         'tag_200_code_f_873510_110730' ,
2261         'tag_200_subfield_f_873510_110730' ,
2262     }
2263     L<$cgi> is the CGI object which containts the value.
2264     L<$record> is the MARC::Record object.
2265
2266 =cut
2267
2268 sub TransformHtmlToMarc {
2269     my $params = shift;
2270     my $cgi    = shift;
2271     
2272     # creating a new record
2273     my $record  = MARC::Record->new();
2274     my $i=0;
2275     my @fields;
2276     while ($params->[$i]){ # browse all CGI params
2277         my $param = $params->[$i];
2278         my $newfield=0;
2279         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
2280         if ($param eq 'biblionumber') {
2281             my ( $biblionumbertagfield, $biblionumbertagsubfield ) =
2282                 &GetMarcFromKohaField( "biblio.biblionumber", '' );
2283             if ($biblionumbertagfield < 10) {
2284                 $newfield = MARC::Field->new(
2285                     $biblionumbertagfield,
2286                     $cgi->param($param),
2287                 );
2288             } else {
2289                 $newfield = MARC::Field->new(
2290                     $biblionumbertagfield,
2291                     '',
2292                     '',
2293                     "$biblionumbertagsubfield" => $cgi->param($param),
2294                 );
2295             }
2296             push @fields,$newfield if($newfield);
2297         } 
2298         elsif ($param =~ /^tag_(\d*)_indicator_/){ # new field start when having 'input name="..._indicator_..."
2299             my $tag  = $1;
2300             
2301             my $ind1 = substr($cgi->param($param),0,1);
2302             my $ind2 = substr($cgi->param($param),1,1);
2303             $newfield=0;
2304             my $j=$i+1;
2305             
2306             if($tag < 10){ # no code for theses fields
2307     # in MARC editor, 000 contains the leader.
2308                 if ($tag eq '000' ) {
2309                     $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==24;
2310     # between 001 and 009 (included)
2311                 } else {
2312                     $newfield = MARC::Field->new(
2313                         $tag,
2314                         $cgi->param($params->[$j+1]),
2315                     );
2316                 }
2317     # > 009, deal with subfields
2318             } else {
2319                 while($params->[$j] =~ /_code_/){ # browse all it's subfield
2320                     my $inner_param = $params->[$j];
2321                     if ($newfield){
2322                         if($cgi->param($params->[$j+1])){  # only if there is a value (code => value)
2323                             $newfield->add_subfields(
2324                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
2325                             );
2326                         }
2327                     } else {
2328                         if ( $cgi->param($params->[$j+1]) ) { # creating only if there is a value (code => value)
2329                             $newfield = MARC::Field->new(
2330                                 $tag,
2331                                 ''.$ind1,
2332                                 ''.$ind2,
2333                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1]),
2334                             );
2335                         }
2336                     }
2337                     $j+=2;
2338                 }
2339             }
2340             push @fields,$newfield if($newfield);
2341         }
2342         $i++;
2343     }
2344     
2345     $record->append_fields(@fields);
2346     return $record;
2347 }
2348
2349 =head2 TransformMarcToKoha
2350
2351 =over 4
2352
2353         $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2354
2355 =back
2356
2357 =cut
2358
2359 sub TransformMarcToKoha {
2360     my ( $dbh, $record, $frameworkcode, $table ) = @_;
2361
2362     my $result;
2363
2364     # sometimes we only want to return the items data
2365     if ($table eq 'items') {
2366         my $sth = $dbh->prepare("SHOW COLUMNS FROM items");
2367         $sth->execute();
2368         while ( (my $field) = $sth->fetchrow ) {
2369             my $value = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2370             my $key = _disambiguate($table, $field);
2371             if ($result->{$key}) {
2372                 $result->{$key} .= " | " . $value;
2373             } else {
2374                 $result->{$key} = $value;
2375             }
2376         }
2377         return $result;
2378     } else {
2379         my @tables = ('biblio','biblioitems','items');
2380         foreach my $table (@tables){
2381             my $sth2 = $dbh->prepare("SHOW COLUMNS from $table");
2382             $sth2->execute;
2383             while (my ($field) = $sth2->fetchrow){
2384                 # FIXME use of _disambiguate is a temporary hack
2385                 # $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2386                 my $value = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2387                 my $key = _disambiguate($table, $field);
2388                 if ($result->{$key}) {
2389                     # FIXME - hack to not bring in duplicates of the same value
2390                     unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
2391                         $result->{$key} .= " | " . $value;
2392                     }
2393                 } else {
2394                     $result->{$key} = $value;
2395                 }
2396             }
2397             $sth2->finish();
2398         }
2399         # modify copyrightdate to keep only the 1st year found
2400         my $temp = $result->{'copyrightdate'};
2401         $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
2402         if ( $1 > 0 ) {
2403             $result->{'copyrightdate'} = $1;
2404         }
2405         else {                      # if no cYYYY, get the 1st date.
2406             $temp =~ m/(\d\d\d\d)/;
2407             $result->{'copyrightdate'} = $1;
2408         }
2409     
2410         # modify publicationyear to keep only the 1st year found
2411         $temp = $result->{'publicationyear'};
2412         $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
2413         if ( $1 > 0 ) {
2414             $result->{'publicationyear'} = $1;
2415         }
2416         else {                      # if no cYYYY, get the 1st date.
2417             $temp =~ m/(\d\d\d\d)/;
2418             $result->{'publicationyear'} = $1;
2419         }
2420         return $result;
2421     }
2422 }
2423
2424
2425 =head2 _disambiguate
2426
2427 =over 4
2428
2429 $newkey = _disambiguate($table, $field);
2430
2431 This is a temporary hack to distinguish between the
2432 following sets of columns when using TransformMarcToKoha.
2433
2434 items.cn_source & biblioitems.cn_source
2435 items.cn_sort & biblioitems.cn_sort
2436
2437 Columns that are currently NOT distinguished (FIXME
2438 due to lack of time to fully test) are:
2439
2440 biblio.notes and biblioitems.notes
2441 biblionumber
2442 timestamp
2443 biblioitemnumber
2444
2445 FIXME - this is necessary because prefixing each column
2446 name with the table name would require changing lots
2447 of code and templates, and exposing more of the DB
2448 structure than is good to the UI templates, particularly
2449 since biblio and bibloitems may well merge in a future
2450 version.  In the future, it would also be good to 
2451 separate DB access and UI presentation field names
2452 more.
2453
2454 =back
2455
2456 =cut
2457
2458 sub _disambiguate {
2459     my ($table, $column) = @_;
2460     if ($column eq "cn_sort" or $column eq "cn_source") {
2461         return $table . '.' . $column;
2462     } else {
2463         return $column;
2464     }
2465
2466 }
2467
2468 =head2 get_koha_field_from_marc
2469
2470 =over 4
2471
2472 $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2473
2474 Internal function to map data from the MARC record to a specific non-MARC field.
2475 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2476
2477 =back
2478
2479 =cut
2480
2481 sub get_koha_field_from_marc {
2482     my ($koha_table,$koha_column,$record,$frameworkcode) = @_;
2483     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table.'.'.$koha_column, $frameworkcode );  
2484     my $kohafield;
2485     foreach my $field ( $record->field($tagfield) ) {
2486         if ( $field->tag() < 10 ) {
2487             if ( $kohafield ) {
2488                 $kohafield .= " | " . $field->data();
2489             }
2490             else {
2491                 $kohafield = $field->data();
2492             }
2493         }
2494         else {
2495             if ( $field->subfields ) {
2496                 my @subfields = $field->subfields();
2497                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2498                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2499                         if ( $kohafield ) {
2500                             $kohafield .=
2501                               " | " . $subfields[$subfieldcount][1];
2502                         }
2503                         else {
2504                             $kohafield =
2505                               $subfields[$subfieldcount][1];
2506                         }
2507                     }
2508                 }
2509             }
2510         }
2511     }
2512     return $kohafield;
2513
2514
2515
2516 =head2 TransformMarcToKohaOneField
2517
2518 =over 4
2519
2520 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2521
2522 =back
2523
2524 =cut
2525
2526 sub TransformMarcToKohaOneField {
2527
2528     # FIXME ? if a field has a repeatable subfield that is used in old-db,
2529     # only the 1st will be retrieved...
2530     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2531     my $res = "";
2532     my ( $tagfield, $subfield ) =
2533       GetMarcFromKohaField( $kohatable . "." . $kohafield,
2534         $frameworkcode );
2535     foreach my $field ( $record->field($tagfield) ) {
2536         if ( $field->tag() < 10 ) {
2537             if ( $result->{$kohafield} ) {
2538                 $result->{$kohafield} .= " | " . $field->data();
2539             }
2540             else {
2541                 $result->{$kohafield} = $field->data();
2542             }
2543         }
2544         else {
2545             if ( $field->subfields ) {
2546                 my @subfields = $field->subfields();
2547                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2548                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2549                         if ( $result->{$kohafield} ) {
2550                             $result->{$kohafield} .=
2551                               " | " . $subfields[$subfieldcount][1];
2552                         }
2553                         else {
2554                             $result->{$kohafield} =
2555                               $subfields[$subfieldcount][1];
2556                         }
2557                     }
2558                 }
2559             }
2560         }
2561     }
2562     return $result;
2563 }
2564
2565 =head1  OTHER FUNCTIONS
2566
2567 =head2 char_decode
2568
2569 =over 4
2570
2571 my $string = char_decode( $string, $encoding );
2572
2573 converts ISO 5426 coded string to UTF-8
2574 sloppy code : should be improved in next issue
2575
2576 =back
2577
2578 =cut
2579
2580 sub char_decode {
2581     my ( $string, $encoding ) = @_;
2582     $_ = $string;
2583
2584     $encoding = C4::Context->preference("marcflavour") unless $encoding;
2585     if ( $encoding eq "UNIMARC" ) {
2586
2587         #         s/\xe1/Æ/gm;
2588         s/\xe2/Ğ/gm;
2589         s/\xe9/Ø/gm;
2590         s/\xec/ş/gm;
2591         s/\xf1/æ/gm;
2592         s/\xf3/ğ/gm;
2593         s/\xf9/ø/gm;
2594         s/\xfb/ß/gm;
2595         s/\xc1\x61/à/gm;
2596         s/\xc1\x65/è/gm;
2597         s/\xc1\x69/ì/gm;
2598         s/\xc1\x6f/ò/gm;
2599         s/\xc1\x75/ù/gm;
2600         s/\xc1\x41/À/gm;
2601         s/\xc1\x45/È/gm;
2602         s/\xc1\x49/Ì/gm;
2603         s/\xc1\x4f/Ò/gm;
2604         s/\xc1\x55/Ù/gm;
2605         s/\xc2\x41/Á/gm;
2606         s/\xc2\x45/É/gm;
2607         s/\xc2\x49/Í/gm;
2608         s/\xc2\x4f/Ó/gm;
2609         s/\xc2\x55/Ú/gm;
2610         s/\xc2\x59/İ/gm;
2611         s/\xc2\x61/á/gm;
2612         s/\xc2\x65/é/gm;
2613         s/\xc2\x69/í/gm;
2614         s/\xc2\x6f/ó/gm;
2615         s/\xc2\x75/ú/gm;
2616         s/\xc2\x79/ı/gm;
2617         s/\xc3\x41/Â/gm;
2618         s/\xc3\x45/Ê/gm;
2619         s/\xc3\x49/Î/gm;
2620         s/\xc3\x4f/Ô/gm;
2621         s/\xc3\x55/Û/gm;
2622         s/\xc3\x61/â/gm;
2623         s/\xc3\x65/ê/gm;
2624         s/\xc3\x69/î/gm;
2625         s/\xc3\x6f/ô/gm;
2626         s/\xc3\x75/û/gm;
2627         s/\xc4\x41/Ã/gm;
2628         s/\xc4\x4e/Ñ/gm;
2629         s/\xc4\x4f/Õ/gm;
2630         s/\xc4\x61/ã/gm;
2631         s/\xc4\x6e/ñ/gm;
2632         s/\xc4\x6f/õ/gm;
2633         s/\xc8\x41/Ä/gm;
2634         s/\xc8\x45/Ë/gm;
2635         s/\xc8\x49/Ï/gm;
2636         s/\xc8\x61/ä/gm;
2637         s/\xc8\x65/ë/gm;
2638         s/\xc8\x69/ï/gm;
2639         s/\xc8\x6F/ö/gm;
2640         s/\xc8\x75/ü/gm;
2641         s/\xc8\x76/ÿ/gm;
2642         s/\xc9\x41/Ä/gm;
2643         s/\xc9\x45/Ë/gm;
2644         s/\xc9\x49/Ï/gm;
2645         s/\xc9\x4f/Ö/gm;
2646         s/\xc9\x55/Ü/gm;
2647         s/\xc9\x61/ä/gm;
2648         s/\xc9\x6f/ö/gm;
2649         s/\xc9\x75/ü/gm;
2650         s/\xca\x41/Å/gm;
2651         s/\xca\x61/å/gm;
2652         s/\xd0\x43/Ç/gm;
2653         s/\xd0\x63/ç/gm;
2654
2655         # this handles non-sorting blocks (if implementation requires this)
2656         $string = nsb_clean($_);
2657     }
2658     elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
2659         ##MARC-8 to UTF-8
2660
2661         s/\xe1\x61/à/gm;
2662         s/\xe1\x65/è/gm;
2663         s/\xe1\x69/ì/gm;
2664         s/\xe1\x6f/ò/gm;
2665         s/\xe1\x75/ù/gm;
2666         s/\xe1\x41/À/gm;
2667         s/\xe1\x45/È/gm;
2668         s/\xe1\x49/Ì/gm;
2669         s/\xe1\x4f/Ò/gm;
2670         s/\xe1\x55/Ù/gm;
2671         s/\xe2\x41/Á/gm;
2672         s/\xe2\x45/É/gm;
2673         s/\xe2\x49/Í/gm;
2674         s/\xe2\x4f/Ó/gm;
2675         s/\xe2\x55/Ú/gm;
2676         s/\xe2\x59/İ/gm;
2677         s/\xe2\x61/á/gm;
2678         s/\xe2\x65/é/gm;
2679         s/\xe2\x69/í/gm;
2680         s/\xe2\x6f/ó/gm;
2681         s/\xe2\x75/ú/gm;
2682         s/\xe2\x79/ı/gm;
2683         s/\xe3\x41/Â/gm;
2684         s/\xe3\x45/Ê/gm;
2685         s/\xe3\x49/Î/gm;
2686         s/\xe3\x4f/Ô/gm;
2687         s/\xe3\x55/Û/gm;
2688         s/\xe3\x61/â/gm;
2689         s/\xe3\x65/ê/gm;
2690         s/\xe3\x69/î/gm;
2691         s/\xe3\x6f/ô/gm;
2692         s/\xe3\x75/û/gm;
2693         s/\xe4\x41/Ã/gm;
2694         s/\xe4\x4e/Ñ/gm;
2695         s/\xe4\x4f/Õ/gm;
2696         s/\xe4\x61/ã/gm;
2697         s/\xe4\x6e/ñ/gm;
2698         s/\xe4\x6f/õ/gm;
2699         s/\xe6\x41/Ă/gm;
2700         s/\xe6\x45/Ĕ/gm;
2701         s/\xe6\x65/ĕ/gm;
2702         s/\xe6\x61/ă/gm;
2703         s/\xe8\x45/Ë/gm;
2704         s/\xe8\x49/Ï/gm;
2705         s/\xe8\x65/ë/gm;
2706         s/\xe8\x69/ï/gm;
2707         s/\xe8\x76/ÿ/gm;
2708         s/\xe9\x41/A/gm;
2709         s/\xe9\x4f/O/gm;
2710         s/\xe9\x55/U/gm;
2711         s/\xe9\x61/a/gm;
2712         s/\xe9\x6f/o/gm;
2713         s/\xe9\x75/u/gm;
2714         s/\xea\x41/A/gm;
2715         s/\xea\x61/a/gm;
2716
2717         #Additional Turkish characters
2718         s/\x1b//gm;
2719         s/\x1e//gm;
2720         s/(\xf0)s/\xc5\x9f/gm;
2721         s/(\xf0)S/\xc5\x9e/gm;
2722         s/(\xf0)c/ç/gm;
2723         s/(\xf0)C/Ç/gm;
2724         s/\xe7\x49/\\xc4\xb0/gm;
2725         s/(\xe6)G/\xc4\x9e/gm;
2726         s/(\xe6)g/ğ\xc4\x9f/gm;
2727         s/\xB8/ı/gm;
2728         s/\xB9/£/gm;
2729         s/(\xe8|\xc8)o/ö/gm;
2730         s/(\xe8|\xc8)O/Ö/gm;
2731         s/(\xe8|\xc8)u/ü/gm;
2732         s/(\xe8|\xc8)U/Ü/gm;
2733         s/\xc2\xb8/\xc4\xb1/gm;
2734         s/¸/\xc4\xb1/gm;
2735
2736         # this handles non-sorting blocks (if implementation requires this)
2737         $string = nsb_clean($_);
2738     }
2739     return ($string);
2740 }
2741
2742 =head2 nsb_clean
2743
2744 =over 4
2745
2746 my $string = nsb_clean( $string, $encoding );
2747
2748 =back
2749
2750 =cut
2751
2752 sub nsb_clean {
2753     my $NSB      = '\x88';    # NSB : begin Non Sorting Block
2754     my $NSE      = '\x89';    # NSE : Non Sorting Block end
2755                               # handles non sorting blocks
2756     my ($string) = @_;
2757     $_ = $string;
2758     s/$NSB/(/gm;
2759     s/[ ]{0,1}$NSE/) /gm;
2760     $string = $_;
2761     return ($string);
2762 }
2763
2764 =head2 PrepareItemrecordDisplay
2765
2766 =over 4
2767
2768 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber);
2769
2770 Returns a hash with all the fields for Display a given item data in a template
2771
2772 =back
2773
2774 =cut
2775
2776 sub PrepareItemrecordDisplay {
2777
2778     my ( $bibnum, $itemnum ) = @_;
2779
2780     my $dbh = C4::Context->dbh;
2781     my $frameworkcode = &GetFrameworkCode( $bibnum );
2782     my ( $itemtagfield, $itemtagsubfield ) =
2783       &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2784     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2785     my $itemrecord = GetMarcItem( $bibnum, $itemnum) if ($itemnum);
2786     my @loop_data;
2787     my $authorised_values_sth =
2788       $dbh->prepare(
2789 "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"
2790       );
2791     foreach my $tag ( sort keys %{$tagslib} ) {
2792         my $previous_tag = '';
2793         if ( $tag ne '' ) {
2794             # loop through each subfield
2795             my $cntsubf;
2796             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2797                 next if ( subfield_is_koha_internal_p($subfield) );
2798                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2799                 my %subfield_data;
2800                 $subfield_data{tag}           = $tag;
2801                 $subfield_data{subfield}      = $subfield;
2802                 $subfield_data{countsubfield} = $cntsubf++;
2803                 $subfield_data{kohafield}     =
2804                   $tagslib->{$tag}->{$subfield}->{'kohafield'};
2805
2806          #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2807                 $subfield_data{marc_lib} =
2808                     "<span id=\"error\" title=\""
2809                   . $tagslib->{$tag}->{$subfield}->{lib} . "\">"
2810                   . substr( $tagslib->{$tag}->{$subfield}->{lib}, 0, 12 )
2811                   . "</span>";
2812                 $subfield_data{mandatory} =
2813                   $tagslib->{$tag}->{$subfield}->{mandatory};
2814                 $subfield_data{repeatable} =
2815                   $tagslib->{$tag}->{$subfield}->{repeatable};
2816                 $subfield_data{hidden} = "display:none"
2817                   if $tagslib->{$tag}->{$subfield}->{hidden};
2818                 my ( $x, $value );
2819                 ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord )
2820                   if ($itemrecord);
2821                 $value =~ s/"/&quot;/g;
2822
2823                 # search for itemcallnumber if applicable
2824                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2825                     'items.itemcallnumber'
2826                     && C4::Context->preference('itemcallnumber') )
2827                 {
2828                     my $CNtag =
2829                       substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2830                     my $CNsubfield =
2831                       substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2832                     my $temp = $itemrecord->field($CNtag) if ($itemrecord);
2833                     if ($temp) {
2834                         $value = $temp->subfield($CNsubfield);
2835                     }
2836                 }
2837                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2838                     my @authorised_values;
2839                     my %authorised_lib;
2840
2841                     # builds list, depending on authorised value...
2842                     #---- branch
2843                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
2844                         "branches" )
2845                     {
2846                         if ( ( C4::Context->preference("IndependantBranches") )
2847                             && ( C4::Context->userenv->{flags} != 1 ) )
2848                         {
2849                             my $sth =
2850                               $dbh->prepare(
2851                                                                 "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname"
2852                               );
2853                             $sth->execute( C4::Context->userenv->{branch} );
2854                             push @authorised_values, ""
2855                               unless (
2856                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2857                             while ( my ( $branchcode, $branchname ) =
2858                                 $sth->fetchrow_array )
2859                             {
2860                                 push @authorised_values, $branchcode;
2861                                 $authorised_lib{$branchcode} = $branchname;
2862                             }
2863                         }
2864                         else {
2865                             my $sth =
2866                               $dbh->prepare(
2867                                                                 "SELECT branchcode,branchname FROM branches ORDER BY branchname"
2868                               );
2869                             $sth->execute;
2870                             push @authorised_values, ""
2871                               unless (
2872                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2873                             while ( my ( $branchcode, $branchname ) =
2874                                 $sth->fetchrow_array )
2875                             {
2876                                 push @authorised_values, $branchcode;
2877                                 $authorised_lib{$branchcode} = $branchname;
2878                             }
2879                         }
2880
2881                         #----- itemtypes
2882                     }
2883                     elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
2884                         "itemtypes" )
2885                     {
2886                         my $sth =
2887                           $dbh->prepare(
2888                                                         "SELECT itemtype,description FROM itemtypes ORDER BY description"
2889                           );
2890                         $sth->execute;
2891                         push @authorised_values, ""
2892                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2893                         while ( my ( $itemtype, $description ) =
2894                             $sth->fetchrow_array )
2895                         {
2896                             push @authorised_values, $itemtype;
2897                             $authorised_lib{$itemtype} = $description;
2898                         }
2899
2900                         #---- "true" authorised value
2901                     }
2902                     else {
2903                         $authorised_values_sth->execute(
2904                             $tagslib->{$tag}->{$subfield}->{authorised_value} );
2905                         push @authorised_values, ""
2906                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2907                         while ( my ( $value, $lib ) =
2908                             $authorised_values_sth->fetchrow_array )
2909                         {
2910                             push @authorised_values, $value;
2911                             $authorised_lib{$value} = $lib;
2912                         }
2913                     }
2914                     $subfield_data{marc_value} = CGI::scrolling_list(
2915                         -name     => 'field_value',
2916                         -values   => \@authorised_values,
2917                         -default  => "$value",
2918                         -labels   => \%authorised_lib,
2919                         -size     => 1,
2920                         -tabindex => '',
2921                         -multiple => 0,
2922                     );
2923                 }
2924                 elsif ( $tagslib->{$tag}->{$subfield}->{thesaurus_category} ) {
2925                     $subfield_data{marc_value} =
2926 "<input type=\"text\" name=\"field_value\"  size=47 maxlength=255> <a href=\"javascript:Dopop('cataloguing/thesaurus_popup.pl?category=$tagslib->{$tag}->{$subfield}->{thesaurus_category}&index=',)\">...</a>";
2927
2928 #"
2929 # COMMENTED OUT because No $i is provided with this API.
2930 # And thus, no value_builder can be activated.
2931 # BUT could be thought over.
2932 #         } elsif ($tagslib->{$tag}->{$subfield}->{'value_builder'}) {
2933 #             my $plugin="value_builder/".$tagslib->{$tag}->{$subfield}->{'value_builder'};
2934 #             require $plugin;
2935 #             my $extended_param = plugin_parameters($dbh,$itemrecord,$tagslib,$i,0);
2936 #             my ($function_name,$javascript) = plugin_javascript($dbh,$record,$tagslib,$i,0);
2937 #             $subfield_data{marc_value}="<input type=\"text\" value=\"$value\" name=\"field_value\"  size=47 maxlength=255 DISABLE READONLY OnFocus=\"javascript:Focus$function_name()\" OnBlur=\"javascript:Blur$function_name()\"> <a href=\"javascript:Clic$function_name()\">...</a> $javascript";
2938                 }
2939                 else {
2940                     $subfield_data{marc_value} =
2941 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=50 maxlength=255>";
2942                 }
2943                 push( @loop_data, \%subfield_data );
2944             }
2945         }
2946     }
2947     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2948       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2949     return {
2950         'itemtagfield'    => $itemtagfield,
2951         'itemtagsubfield' => $itemtagsubfield,
2952         'itemnumber'      => $itemnumber,
2953         'iteminformation' => \@loop_data
2954     };
2955 }
2956 #"
2957
2958 #
2959 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2960 # at the same time
2961 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2962 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2963 # =head2 ModZebrafiles
2964
2965 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2966
2967 # =cut
2968
2969 # sub ModZebrafiles {
2970
2971 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2972
2973 #     my $op;
2974 #     my $zebradir =
2975 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2976 #     unless ( opendir( DIR, "$zebradir" ) ) {
2977 #         warn "$zebradir not found";
2978 #         return;
2979 #     }
2980 #     closedir DIR;
2981 #     my $filename = $zebradir . $biblionumber;
2982
2983 #     if ($record) {
2984 #         open( OUTPUT, ">", $filename . ".xml" );
2985 #         print OUTPUT $record;
2986 #         close OUTPUT;
2987 #     }
2988 # }
2989
2990 =head2 ModZebra
2991
2992 =over 4
2993
2994 ModZebra( $biblionumber, $op, $server, $newRecord );
2995
2996     $biblionumber is the biblionumber we want to index
2997     $op is specialUpdate or delete, and is used to know what we want to do
2998     $server is the server that we want to update
2999     $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.
3000     
3001 =back
3002
3003 =cut
3004
3005 sub ModZebra {
3006 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
3007     my ( $biblionumber, $op, $server, $newRecord ) = @_;
3008     my $dbh=C4::Context->dbh;
3009
3010     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
3011     # at the same time
3012     # replaced by a zebraqueue table, that is filled with ModZebra to run.
3013     # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
3014
3015     if (C4::Context->preference("NoZebra")) {
3016         # lock the nozebra table : we will read index lines, update them in Perl process
3017         # and write everything in 1 transaction.
3018         # lock the table to avoid someone else overwriting what we are doing
3019         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE');
3020         my %result; # the result hash that will be builded by deletion / add, and written on mySQL at the end, to improve speed
3021         my $record;
3022         if ($server eq 'biblioserver') {
3023             $record= GetMarcBiblio($biblionumber);
3024         } else {
3025             $record= C4::AuthoritiesMarc::GetAuthority($biblionumber);
3026         }
3027         if ($op eq 'specialUpdate') {
3028             # OK, we have to add or update the record
3029             # 1st delete (virtually, in indexes) ...
3030             %result = _DelBiblioNoZebra($biblionumber,$record,$server);
3031             # ... add the record
3032             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
3033         } else {
3034             # it's a deletion, delete the record...
3035             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
3036             %result=_DelBiblioNoZebra($biblionumber,$record,$server);
3037         }
3038         # ok, now update the database...
3039         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
3040         foreach my $key (keys %result) {
3041             foreach my $index (keys %{$result{$key}}) {
3042                 $sth->execute($result{$key}->{$index}, $server, $key, $index);
3043             }
3044         }
3045         $dbh->do('UNLOCK TABLES');
3046
3047     } else {
3048         #
3049         # we use zebra, just fill zebraqueue table
3050         #
3051         my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
3052         $sth->execute($biblionumber,$server,$op);
3053         $sth->finish;
3054     }
3055 }
3056
3057 =head2 GetNoZebraIndexes
3058
3059     %indexes = GetNoZebraIndexes;
3060     
3061     return the data from NoZebraIndexes syspref.
3062
3063 =cut
3064
3065 sub GetNoZebraIndexes {
3066     my $index = C4::Context->preference('NoZebraIndexes');
3067     my %indexes;
3068     foreach my $line (split /('|"),/,$index) {
3069         $line =~ /(.*)=>(.*)/;
3070         my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
3071         my $fields = $2;
3072         $index =~ s/'|"| //g;
3073         $fields =~ s/'|"| //g;
3074         $indexes{$index}=$fields;
3075     }
3076     return %indexes;
3077 }
3078
3079 =head1 INTERNAL FUNCTIONS
3080
3081 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
3082
3083     function to delete a biblio in NoZebra indexes
3084     This function does NOT delete anything in database : it reads all the indexes entries
3085     that have to be deleted & delete them in the hash
3086     The SQL part is done either :
3087     - after the Add if we are modifying a biblio (delete + add again)
3088     - immediatly after this sub if we are doing a true deletion.
3089     $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
3090
3091 =cut
3092
3093
3094 sub _DelBiblioNoZebra {
3095     my ($biblionumber, $record, $server)=@_;
3096     
3097     # Get the indexes
3098     my $dbh = C4::Context->dbh;
3099     # Get the indexes
3100     my %index;
3101     my $title;
3102     if ($server eq 'biblioserver') {
3103         %index=GetNoZebraIndexes;
3104         # get title of the record (to store the 10 first letters with the index)
3105         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
3106         $title = lc($record->subfield($titletag,$titlesubfield));
3107     } else {
3108         # for authorities, the "title" is the $a mainentry
3109         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
3110         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
3111         $title = $record->subfield($authref->{auth_tag_to_report},'a');
3112         $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
3113         $index{'mainentry'}    = $authref->{'auth_tag_to_report'}.'*';
3114         $index{'auth_type'}    = '152b';
3115     }
3116     
3117     my %result;
3118     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
3119     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
3120     # limit to 10 char, should be enough, and limit the DB size
3121     $title = substr($title,0,10);
3122     #parse each field
3123     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
3124     foreach my $field ($record->fields()) {
3125         #parse each subfield
3126         next if $field->tag <10;
3127         foreach my $subfield ($field->subfields()) {
3128             my $tag = $field->tag();
3129             my $subfieldcode = $subfield->[0];
3130             my $indexed=0;
3131             # check each index to see if the subfield is stored somewhere
3132             # otherwise, store it in __RAW__ index
3133             foreach my $key (keys %index) {
3134 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
3135                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
3136                     $indexed=1;
3137                     my $line= lc $subfield->[1];
3138                     # remove meaningless value in the field...
3139                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3140                     # ... and split in words
3141                     foreach (split / /,$line) {
3142                         next unless $_; # skip  empty values (multiple spaces)
3143                         # if the entry is already here, do nothing, the biblionumber has already be removed
3144                         unless ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
3145                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
3146                             $sth2->execute($server,$key,$_);
3147                             my $existing_biblionumbers = $sth2->fetchrow;
3148                             # it exists
3149                             if ($existing_biblionumbers) {
3150 #                                 warn " existing for $key $_: $existing_biblionumbers";
3151                                 $result{$key}->{$_} =$existing_biblionumbers;
3152                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3153                             }
3154                         }
3155                     }
3156                 }
3157             }
3158             # the subfield is not indexed, store it in __RAW__ index anyway
3159             unless ($indexed) {
3160                 my $line= lc $subfield->[1];
3161                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3162                 # ... and split in words
3163                 foreach (split / /,$line) {
3164                     next unless $_; # skip  empty values (multiple spaces)
3165                     # if the entry is already here, do nothing, the biblionumber has already be removed
3166                     unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
3167                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
3168                         $sth2->execute($server,'__RAW__',$_);
3169                         my $existing_biblionumbers = $sth2->fetchrow;
3170                         # it exists
3171                         if ($existing_biblionumbers) {
3172                             $result{'__RAW__'}->{$_} =$existing_biblionumbers;
3173                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3174                         }
3175                     }
3176                 }
3177             }
3178         }
3179     }
3180     return %result;
3181 }
3182
3183 =head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
3184
3185     function to add a biblio in NoZebra indexes
3186
3187 =cut
3188
3189 sub _AddBiblioNoZebra {
3190     my ($biblionumber, $record, $server, %result)=@_;
3191     my $dbh = C4::Context->dbh;
3192     # Get the indexes
3193     my %index;
3194     my $title;
3195     if ($server eq 'biblioserver') {
3196         %index=GetNoZebraIndexes;
3197         # get title of the record (to store the 10 first letters with the index)
3198         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
3199         $title = lc($record->subfield($titletag,$titlesubfield));
3200     } else {
3201         # warn "server : $server";
3202         # for authorities, the "title" is the $a mainentry
3203         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
3204         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
3205         $title = $record->subfield($authref->{auth_tag_to_report},'a');
3206         $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
3207         $index{'mainentry'}     = $authref->{auth_tag_to_report}.'*';
3208         $index{'auth_type'}     = '152b';
3209     }
3210
3211     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
3212     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
3213     # limit to 10 char, should be enough, and limit the DB size
3214     $title = substr($title,0,10);
3215     #parse each field
3216     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
3217     foreach my $field ($record->fields()) {
3218         #parse each subfield
3219         next if $field->tag <10;
3220         foreach my $subfield ($field->subfields()) {
3221             my $tag = $field->tag();
3222             my $subfieldcode = $subfield->[0];
3223             my $indexed=0;
3224             # check each index to see if the subfield is stored somewhere
3225             # otherwise, store it in __RAW__ index
3226             foreach my $key (keys %index) {
3227 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
3228                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
3229                     $indexed=1;
3230                     my $line= lc $subfield->[1];
3231                     # remove meaningless value in the field...
3232                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3233                     # ... and split in words
3234                     foreach (split / /,$line) {
3235                         next unless $_; # skip  empty values (multiple spaces)
3236                         # if the entry is already here, improve weight
3237 #                         warn "managing $_";
3238                         if ($result{$key}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
3239                             my $weight=$1+1;
3240                             $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3241                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
3242                         } else {
3243                             # get the value if it exist in the nozebra table, otherwise, create it
3244                             $sth2->execute($server,$key,$_);
3245                             my $existing_biblionumbers = $sth2->fetchrow;
3246                             # it exists
3247                             if ($existing_biblionumbers) {
3248                                 $result{$key}->{"$_"} =$existing_biblionumbers;
3249                                 my $weight=$1+1;
3250                                 $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3251                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
3252                             # create a new ligne for this entry
3253                             } else {
3254 #                             warn "INSERT : $server / $key / $_";
3255                                 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
3256                                 $result{$key}->{"$_"}.="$biblionumber,$title-1;";
3257                             }
3258                         }
3259                     }
3260                 }
3261             }
3262             # the subfield is not indexed, store it in __RAW__ index anyway
3263             unless ($indexed) {
3264                 my $line= lc $subfield->[1];
3265                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3266                 # ... and split in words
3267                 foreach (split / /,$line) {
3268                     next unless $_; # skip  empty values (multiple spaces)
3269                     # if the entry is already here, improve weight
3270                     if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
3271                         my $weight=$1+1;
3272                         $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3273                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
3274                     } else {
3275                         # get the value if it exist in the nozebra table, otherwise, create it
3276                         $sth2->execute($server,'__RAW__',$_);
3277                         my $existing_biblionumbers = $sth2->fetchrow;
3278                         # it exists
3279                         if ($existing_biblionumbers) {
3280                             $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
3281                             my $weight=$1+1;
3282                             $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3283                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
3284                         # create a new ligne for this entry
3285                         } else {
3286                             $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).',  indexname="__RAW__",value='.$dbh->quote($_));
3287                             $result{'__RAW__'}->{"$_"}.="$biblionumber,$title-1;";
3288                         }
3289                     }
3290                 }
3291             }
3292         }
3293     }
3294     return %result;
3295 }
3296
3297
3298 =head2 MARCitemchange
3299
3300 =over 4
3301
3302 &MARCitemchange( $record, $itemfield, $newvalue )
3303
3304 Function to update a single value in an item field.
3305 Used twice, could probably be replaced by something else, but works well...
3306
3307 =back
3308
3309 =back
3310
3311 =cut
3312
3313 sub MARCitemchange {
3314     my ( $record, $itemfield, $newvalue ) = @_;
3315     my $dbh = C4::Context->dbh;
3316     
3317     my ( $tagfield, $tagsubfield ) =
3318       GetMarcFromKohaField( $itemfield, "" );
3319     if ( ($tagfield) && ($tagsubfield) ) {
3320         my $tag = $record->field($tagfield);
3321         if ($tag) {
3322             $tag->update( $tagsubfield => $newvalue );
3323             $record->delete_field($tag);
3324             $record->insert_fields_ordered($tag);
3325         }
3326     }
3327 }
3328 =head2 _find_value
3329
3330 =over 4
3331
3332 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
3333
3334 Find the given $subfield in the given $tag in the given
3335 MARC::Record $record.  If the subfield is found, returns
3336 the (indicators, value) pair; otherwise, (undef, undef) is
3337 returned.
3338
3339 PROPOSITION :
3340 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
3341 I suggest we export it from this module.
3342
3343 =back
3344
3345 =cut
3346
3347 sub _find_value {
3348     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
3349     my @result;
3350     my $indicator;
3351     if ( $tagfield < 10 ) {
3352         if ( $record->field($tagfield) ) {
3353             push @result, $record->field($tagfield)->data();
3354         }
3355         else {
3356             push @result, "";
3357         }
3358     }
3359     else {
3360         foreach my $field ( $record->field($tagfield) ) {
3361             my @subfields = $field->subfields();
3362             foreach my $subfield (@subfields) {
3363                 if ( @$subfield[0] eq $insubfield ) {
3364                     push @result, @$subfield[1];
3365                     $indicator = $field->indicator(1) . $field->indicator(2);
3366                 }
3367             }
3368         }
3369     }
3370     return ( $indicator, @result );
3371 }
3372
3373 =head2 _koha_marc_update_bib_ids
3374
3375 =over 4
3376
3377 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
3378
3379 Internal function to add or update biblionumber and biblioitemnumber to
3380 the MARC XML.
3381
3382 =back
3383
3384 =cut
3385
3386 sub _koha_marc_update_bib_ids {
3387     my ($record, $frameworkcode, $biblionumber, $biblioitemnumber) = @_;
3388
3389     # we must add bibnum and bibitemnum in MARC::Record...
3390     # we build the new field with biblionumber and biblioitemnumber
3391     # we drop the original field
3392     # we add the new builded field.
3393     my ($biblio_tag, $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
3394     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
3395
3396     if ($biblio_tag != $biblioitem_tag) {
3397         # biblionumber & biblioitemnumber are in different fields
3398
3399         # deal with biblionumber
3400         my ($new_field, $old_field);
3401         if ($biblio_tag < 10) {
3402             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
3403         } else {
3404             $new_field =
3405               MARC::Field->new( $biblio_tag, '', '',
3406                 "$biblio_subfield" => $biblionumber );
3407         }
3408
3409         # drop old field and create new one...
3410         $old_field = $record->field($biblio_tag);
3411         $record->delete_field($old_field);
3412         $record->append_fields($new_field);
3413
3414         # deal with biblioitemnumber
3415         if ($biblioitem_tag < 10) {
3416             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
3417         } else {
3418             $new_field =
3419               MARC::Field->new( $biblioitem_tag, '', '',
3420                 "$biblioitem_subfield" => $biblioitemnumber, );
3421         }
3422         # drop old field and create new one...
3423         $old_field = $record->field($biblioitem_tag);
3424         $record->delete_field($old_field);
3425         $record->insert_fields_ordered($new_field);
3426
3427     } else {
3428         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
3429         my $new_field = MARC::Field->new(
3430             $biblio_tag, '', '',
3431             "$biblio_subfield" => $biblionumber,
3432             "$biblioitem_subfield" => $biblioitemnumber
3433         );
3434
3435         # drop old field and create new one...
3436         my $old_field = $record->field($biblio_tag);
3437         $record->delete_field($old_field);
3438         $record->insert_fields_ordered($new_field);
3439     }
3440 }
3441
3442 =head2 _koha_add_biblio
3443
3444 =over 4
3445
3446 my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
3447
3448 Internal function to add a biblio ($biblio is a hash with the values)
3449
3450 =back
3451
3452 =cut
3453
3454 sub _koha_add_biblio {
3455     my ( $dbh, $biblio, $frameworkcode ) = @_;
3456
3457         my $error;
3458
3459         # get the next biblionumber
3460     my $sth = $dbh->prepare("SELECT MAX(biblionumber) FROM biblio");
3461     $sth->execute();
3462     my $data = $sth->fetchrow_arrayref();
3463     my $biblionumber = $$data[0] + 1;
3464         # set the series flag
3465     my $serial = 0;
3466     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
3467
3468     $sth->finish();
3469         my $query = 
3470         "INSERT INTO biblio
3471                 SET biblionumber  = ?, 
3472                         frameworkcode = ?,
3473                         author = ?,
3474                         title = ?,
3475                         unititle =?,
3476                         notes = ?,
3477                         serial = ?,
3478                         seriestitle = ?,
3479                         copyrightdate = ?,
3480                         datecreated=NOW(),
3481                         abstract = ?
3482                 ";
3483     $sth = $dbh->prepare($query);
3484     $sth->execute(
3485         $biblionumber,
3486                 $frameworkcode,
3487         $biblio->{'author'},
3488         $biblio->{'title'},
3489                 $biblio->{'unititle'},
3490         $biblio->{'notes'},
3491                 $serial,
3492         $biblio->{'seriestitle'},
3493                 $biblio->{'copyrightdate'},
3494         $biblio->{'abstract'}
3495     );
3496
3497         if ( $dbh->errstr ) {
3498                 $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
3499         warn $error;
3500     }
3501
3502     $sth->finish();
3503         #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3504     return ($biblionumber,$error);
3505 }
3506
3507 =head2 _koha_modify_biblio
3508
3509 =over 4
3510
3511 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3512
3513 Internal function for updating the biblio table
3514
3515 =back
3516
3517 =cut
3518
3519 sub _koha_modify_biblio {
3520     my ( $dbh, $biblio, $frameworkcode ) = @_;
3521         my $error;
3522
3523     my $query = "
3524         UPDATE biblio
3525         SET    frameworkcode = ?,
3526                            author = ?,
3527                            title = ?,
3528                            unititle = ?,
3529                            notes = ?,
3530                            serial = ?,
3531                            seriestitle = ?,
3532                            copyrightdate = ?,
3533                abstract = ?
3534         WHERE  biblionumber = ?
3535                 "
3536         ;
3537     my $sth = $dbh->prepare($query);
3538     
3539     $sth->execute(
3540                 $frameworkcode,
3541         $biblio->{'author'},
3542         $biblio->{'title'},
3543         $biblio->{'unititle'},
3544         $biblio->{'notes'},
3545         $biblio->{'serial'},
3546         $biblio->{'seriestitle'},
3547         $biblio->{'copyrightdate'},
3548                 $biblio->{'abstract'},
3549         $biblio->{'biblionumber'}
3550     ) if $biblio->{'biblionumber'};
3551
3552     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3553                 $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
3554         warn $error;
3555     }
3556     return ( $biblio->{'biblionumber'},$error );
3557 }
3558
3559 =head2 _koha_modify_biblioitem_nonmarc
3560
3561 =over 4
3562
3563 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3564
3565 Updates biblioitems row except for marc and marcxml, which should be changed
3566 via ModBiblioMarc
3567
3568 =back
3569
3570 =cut
3571
3572 sub _koha_modify_biblioitem_nonmarc {
3573     my ( $dbh, $biblioitem ) = @_;
3574         my $error;
3575
3576         # re-calculate the cn_sort, it may have changed
3577         my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3578
3579         my $query = 
3580         "UPDATE biblioitems 
3581         SET biblionumber        = ?,
3582                 volume                  = ?,
3583                 number                  = ?,
3584         itemtype        = ?,
3585         isbn            = ?,
3586         issn            = ?,
3587                 publicationyear = ?,
3588         publishercode   = ?,
3589                 volumedate      = ?,
3590                 volumedesc      = ?,
3591                 collectiontitle = ?,
3592                 collectionissn  = ?,
3593                 collectionvolume= ?,
3594                 editionstatement= ?,
3595                 editionresponsibility = ?,
3596                 illus                   = ?,
3597                 pages                   = ?,
3598                 notes                   = ?,
3599                 size                    = ?,
3600                 place                   = ?,
3601                 lccn                    = ?,
3602                 url                     = ?,
3603         cn_source               = ?,
3604         cn_class        = ?,
3605         cn_item         = ?,
3606                 cn_suffix       = ?,
3607                 cn_sort         = ?,
3608                 totalissues     = ?
3609         where biblioitemnumber = ?
3610                 ";
3611         my $sth = $dbh->prepare($query);
3612         $sth->execute(
3613                 $biblioitem->{'biblionumber'},
3614                 $biblioitem->{'volume'},
3615                 $biblioitem->{'number'},
3616                 $biblioitem->{'itemtype'},
3617                 $biblioitem->{'isbn'},
3618                 $biblioitem->{'issn'},
3619                 $biblioitem->{'publicationyear'},
3620                 $biblioitem->{'publishercode'},
3621                 $biblioitem->{'volumedate'},
3622                 $biblioitem->{'volumedesc'},
3623                 $biblioitem->{'collectiontitle'},
3624                 $biblioitem->{'collectionissn'},
3625                 $biblioitem->{'collectionvolume'},
3626                 $biblioitem->{'editionstatement'},
3627                 $biblioitem->{'editionresponsibility'},
3628                 $biblioitem->{'illus'},
3629                 $biblioitem->{'pages'},
3630                 $biblioitem->{'bnotes'},
3631                 $biblioitem->{'size'},
3632                 $biblioitem->{'place'},
3633                 $biblioitem->{'lccn'},
3634                 $biblioitem->{'url'},
3635                 $biblioitem->{'biblioitems.cn_source'},
3636                 $biblioitem->{'cn_class'},
3637                 $biblioitem->{'cn_item'},
3638                 $biblioitem->{'cn_suffix'},
3639                 $cn_sort,
3640                 $biblioitem->{'totalissues'},
3641                 $biblioitem->{'biblioitemnumber'}
3642         );
3643     if ( $dbh->errstr ) {
3644                 $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
3645         warn $error;
3646     }
3647         return ($biblioitem->{'biblioitemnumber'},$error);
3648 }
3649
3650 =head2 _koha_add_biblioitem
3651
3652 =over 4
3653
3654 my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3655
3656 Internal function to add a biblioitem
3657
3658 =back
3659
3660 =cut
3661
3662 sub _koha_add_biblioitem {
3663     my ( $dbh, $biblioitem ) = @_;
3664         my $error;
3665     my $sth = $dbh->prepare("SELECT MAX(biblioitemnumber) FROM biblioitems");
3666     $sth->execute();
3667     my $data       = $sth->fetchrow_arrayref;
3668     my $bibitemnum = $$data[0] + 1;
3669     $sth->finish();
3670
3671         my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3672     my $query =
3673     "INSERT INTO biblioitems SET
3674                 biblioitemnumber = ?,
3675         biblionumber    = ?,
3676         volume          = ?,
3677         number          = ?,
3678         itemtype        = ?,
3679         isbn            = ?,
3680         issn            = ?,
3681         publicationyear = ?,
3682         publishercode   = ?,
3683         volumedate      = ?,
3684         volumedesc      = ?,
3685         collectiontitle = ?,
3686         collectionissn  = ?,
3687         collectionvolume= ?,
3688         editionstatement= ?,
3689         editionresponsibility = ?,
3690         illus           = ?,
3691         pages           = ?,
3692         notes           = ?,
3693         size            = ?,
3694         place           = ?,
3695         lccn            = ?,
3696         marc            = ?,
3697         url             = ?,
3698         cn_source       = ?,
3699         cn_class        = ?,
3700         cn_item         = ?,
3701         cn_suffix       = ?,
3702         cn_sort         = ?,
3703         totalissues     = ?
3704         ";
3705         $sth = $dbh->prepare($query);
3706     $sth->execute(
3707                 $bibitemnum,
3708         $biblioitem->{'biblionumber'},
3709         $biblioitem->{'volume'},
3710         $biblioitem->{'number'},
3711         $biblioitem->{'itemtype'},
3712         $biblioitem->{'isbn'},
3713         $biblioitem->{'issn'},
3714         $biblioitem->{'publicationyear'},
3715         $biblioitem->{'publishercode'},
3716         $biblioitem->{'volumedate'},
3717         $biblioitem->{'volumedesc'},
3718         $biblioitem->{'collectiontitle'},
3719         $biblioitem->{'collectionissn'},
3720         $biblioitem->{'collectionvolume'},
3721         $biblioitem->{'editionstatement'},
3722         $biblioitem->{'editionresponsibility'},
3723         $biblioitem->{'illus'},
3724         $biblioitem->{'pages'},
3725         $biblioitem->{'bnotes'},
3726         $biblioitem->{'size'},
3727         $biblioitem->{'place'},
3728         $biblioitem->{'lccn'},
3729         $biblioitem->{'marc'},
3730         $biblioitem->{'url'},
3731         $biblioitem->{'biblioitems.cn_source'},
3732         $biblioitem->{'cn_class'},
3733         $biblioitem->{'cn_item'},
3734         $biblioitem->{'cn_suffix'},
3735         $cn_sort,
3736         $biblioitem->{'totalissues'}
3737     );
3738     if ( $dbh->errstr ) {
3739                 $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
3740                 warn $error;
3741     }
3742     $sth->finish();
3743     return ($bibitemnum,$error);
3744 }
3745
3746 =head2 _koha_new_items
3747
3748 =over 4
3749
3750 my ($itemnumber,$error) = _koha_new_items( $dbh, $item, $barcode );
3751
3752 =back
3753
3754 =cut
3755
3756 sub _koha_new_items {
3757     my ( $dbh, $item, $barcode ) = @_;
3758         my $error;
3759
3760     my $sth = $dbh->prepare("SELECT MAX(itemnumber) FROM items");
3761     $sth->execute();
3762     my $data       = $sth->fetchrow_hashref;
3763     my $itemnumber = $data->{'MAX(itemnumber)'} + 1;
3764     $sth->finish;
3765
3766     my ($items_cn_sort) = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
3767
3768     # if dateaccessioned is provided, use it. Otherwise, set to NOW()
3769     if ( $item->{'dateaccessioned'} eq '' || !$item->{'dateaccessioned'} ) {
3770                 my $today = C4::Dates->new();    
3771                 $item->{'dateaccessioned'} =  $today->output("iso"); #TODO: check time issues
3772         }
3773         my $query = 
3774            "INSERT INTO items SET
3775             itemnumber          = ?,
3776                         biblionumber            = ?,
3777             biblioitemnumber    = ?,
3778                         barcode                 = ?,
3779                         dateaccessioned         = ?,
3780                         booksellerid        = ?,
3781             homebranch          = ?,
3782             price               = ?,
3783                         replacementprice        = ?,
3784             replacementpricedate = NOW(),
3785                         datelastborrowed        = ?,
3786                         datelastseen            = NOW(),
3787                         stack                   = ?,
3788                         notforloan                      = ?,
3789                         damaged                         = ?,
3790             itemlost            = ?,
3791                         wthdrawn                = ?,
3792                         itemcallnumber          = ?,
3793                         restricted                      = ?,
3794                         itemnotes                       = ?,
3795                         holdingbranch           = ?,
3796             paidfor             = ?,
3797                         location                        = ?,
3798                         onloan                          = ?,
3799                         cn_source                       = ?,
3800                         cn_sort                         = ?,
3801                         ccode                           = ?,
3802                         materials                       = ?,
3803                         uri                             = ?
3804           ";
3805     $sth = $dbh->prepare($query);
3806         $sth->execute(
3807             $itemnumber,
3808                         $item->{'biblionumber'},
3809                         $item->{'biblioitemnumber'},
3810             $barcode,
3811                         $item->{'dateaccessioned'},
3812                         $item->{'booksellerid'},
3813             $item->{'homebranch'},
3814             $item->{'price'},
3815                         $item->{'replacementprice'},
3816                         $item->{datelastborrowed},
3817                         $item->{stack},
3818                         $item->{'notforloan'},
3819                         $item->{'damaged'},
3820             $item->{'itemlost'},
3821                         $item->{'wthdrawn'},
3822                         $item->{'itemcallnumber'},
3823             $item->{'restricted'},
3824                         $item->{'itemnotes'},
3825                         $item->{'holdingbranch'},
3826                         $item->{'paidfor'},
3827                         $item->{'location'},
3828                         $item->{'onloan'},
3829                         $item->{'items.cn_source'},
3830                         $items_cn_sort,
3831                         $item->{'ccode'},
3832                         $item->{'materials'},
3833                         $item->{'uri'},
3834     );
3835     if ( defined $sth->errstr ) {
3836         $error.="ERROR in _koha_new_items $query".$sth->errstr;
3837     }
3838         $sth->finish();
3839     return ( $itemnumber, $error );
3840 }
3841
3842 =head2 _koha_modify_item
3843
3844 =over 4
3845
3846 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
3847
3848 =back
3849
3850 =cut
3851
3852 sub _koha_modify_item {
3853     my ( $dbh, $item ) = @_;
3854         my $error;
3855
3856         # calculate items.cn_sort
3857     $item->{'cn_sort'} = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
3858
3859     my $query = "UPDATE items SET ";
3860         my @bind;
3861         for my $key ( keys %$item ) {
3862                 $query.="$key=?,";
3863                 push @bind, $item->{$key};
3864     }
3865         $query =~ s/,$//;
3866     $query .= " WHERE itemnumber=?";
3867     push @bind, $item->{'itemnumber'};
3868     my $sth = $dbh->prepare($query);
3869     $sth->execute(@bind);
3870     if ( $dbh->errstr ) {
3871         $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
3872         warn $error;
3873     }
3874     $sth->finish();
3875         return ($item->{'itemnumber'},$error);
3876 }
3877
3878 =head2 _koha_delete_biblio
3879
3880 =over 4
3881
3882 $error = _koha_delete_biblio($dbh,$biblionumber);
3883
3884 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3885
3886 C<$dbh> - the database handle
3887 C<$biblionumber> - the biblionumber of the biblio to be deleted
3888
3889 =back
3890
3891 =cut
3892
3893 # FIXME: add error handling
3894
3895 sub _koha_delete_biblio {
3896     my ( $dbh, $biblionumber ) = @_;
3897
3898     # get all the data for this biblio
3899     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3900     $sth->execute($biblionumber);
3901
3902     if ( my $data = $sth->fetchrow_hashref ) {
3903
3904         # save the record in deletedbiblio
3905         # find the fields to save
3906         my $query = "INSERT INTO deletedbiblio SET ";
3907         my @bind  = ();
3908         foreach my $temp ( keys %$data ) {
3909             $query .= "$temp = ?,";
3910             push( @bind, $data->{$temp} );
3911         }
3912
3913         # replace the last , by ",?)"
3914         $query =~ s/\,$//;
3915         my $bkup_sth = $dbh->prepare($query);
3916         $bkup_sth->execute(@bind);
3917         $bkup_sth->finish;
3918
3919         # delete the biblio
3920         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3921         $del_sth->execute($biblionumber);
3922         $del_sth->finish;
3923     }
3924     $sth->finish;
3925     return undef;
3926 }
3927
3928 =head2 _koha_delete_biblioitems
3929
3930 =over 4
3931
3932 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3933
3934 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3935
3936 C<$dbh> - the database handle
3937 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3938
3939 =back
3940
3941 =cut
3942
3943 # FIXME: add error handling
3944
3945 sub _koha_delete_biblioitems {
3946     my ( $dbh, $biblioitemnumber ) = @_;
3947
3948     # get all the data for this biblioitem
3949     my $sth =
3950       $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3951     $sth->execute($biblioitemnumber);
3952
3953     if ( my $data = $sth->fetchrow_hashref ) {
3954
3955         # save the record in deletedbiblioitems
3956         # find the fields to save
3957         my $query = "INSERT INTO deletedbiblioitems SET ";
3958         my @bind  = ();
3959         foreach my $temp ( keys %$data ) {
3960             $query .= "$temp = ?,";
3961             push( @bind, $data->{$temp} );
3962         }
3963
3964         # replace the last , by ",?)"
3965         $query =~ s/\,$//;
3966         my $bkup_sth = $dbh->prepare($query);
3967         $bkup_sth->execute(@bind);
3968         $bkup_sth->finish;
3969
3970         # delete the biblioitem
3971         my $del_sth =
3972           $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3973         $del_sth->execute($biblioitemnumber);
3974         $del_sth->finish;
3975     }
3976     $sth->finish;
3977     return undef;
3978 }
3979
3980 =head2 _koha_delete_item
3981
3982 =over 4
3983
3984 _koha_delete_item( $dbh, $itemnum );
3985
3986 Internal function to delete an item record from the koha tables
3987
3988 =back
3989
3990 =cut
3991
3992 sub _koha_delete_item {
3993     my ( $dbh, $itemnum ) = @_;
3994
3995         # save the deleted item to deleteditems table
3996     my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
3997     $sth->execute($itemnum);
3998     my $data = $sth->fetchrow_hashref();
3999     $sth->finish();
4000     my $query = "INSERT INTO deleteditems SET ";
4001     my @bind  = ();
4002     foreach my $key ( keys %$data ) {
4003         $query .= "$key = ?,";
4004         push( @bind, $data->{$key} );
4005     }
4006     $query =~ s/\,$//;
4007     $sth = $dbh->prepare($query);
4008     $sth->execute(@bind);
4009     $sth->finish();
4010
4011         # delete from items table
4012     $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
4013     $sth->execute($itemnum);
4014     $sth->finish();
4015         return undef;
4016 }
4017
4018 =head1 UNEXPORTED FUNCTIONS
4019
4020 =head2 ModBiblioMarc
4021
4022     &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
4023     
4024     Add MARC data for a biblio to koha 
4025     
4026     Function exported, but should NOT be used, unless you really know what you're doing
4027
4028 =cut
4029
4030 sub ModBiblioMarc {
4031     
4032 # pass the MARC::Record to this function, and it will create the records in the marc field
4033     my ( $record, $biblionumber, $frameworkcode ) = @_;
4034     my $dbh = C4::Context->dbh;
4035     my @fields = $record->fields();
4036     if ( !$frameworkcode ) {
4037         $frameworkcode = "";
4038     }
4039     my $sth =
4040       $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
4041     $sth->execute( $frameworkcode, $biblionumber );
4042     $sth->finish;
4043     my $encoding = C4::Context->preference("marcflavour");
4044
4045     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
4046     if ( $encoding eq "UNIMARC" ) {
4047         my $string;
4048         if ( length($record->subfield( 100, "a" )) == 35 ) {
4049             $string = $record->subfield( 100, "a" );
4050             my $f100 = $record->field(100);
4051             $record->delete_field($f100);
4052         }
4053         else {
4054             $string = POSIX::strftime( "%Y%m%d", localtime );
4055             $string =~ s/\-//g;
4056             $string = sprintf( "%-*s", 35, $string );
4057         }
4058         substr( $string, 22, 6, "frey50" );
4059         unless ( $record->subfield( 100, "a" ) ) {
4060             $record->insert_grouped_field(
4061                 MARC::Field->new( 100, "", "", "a" => $string ) );
4062         }
4063     }
4064     ModZebra($biblionumber,"specialUpdate","biblioserver",$record);
4065     $sth =
4066       $dbh->prepare(
4067         "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
4068     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
4069         $biblionumber );
4070     $sth->finish;
4071     return $biblionumber;
4072 }
4073
4074 =head2 AddItemInMarc
4075
4076 =over 4
4077
4078 $newbiblionumber = AddItemInMarc( $record, $biblionumber, $frameworkcode );
4079
4080 Add an item in a MARC record and save the MARC record
4081
4082 Function exported, but should NOT be used, unless you really know what you're doing
4083
4084 =back
4085
4086 =cut
4087
4088 sub AddItemInMarc {
4089
4090     # pass the MARC::Record to this function, and it will create the records in the marc tables
4091     my ( $record, $biblionumber, $frameworkcode ) = @_;
4092     my $newrec = &GetMarcBiblio($biblionumber);
4093
4094     # create it
4095     my @fields = $record->fields();
4096     foreach my $field (@fields) {
4097         $newrec->append_fields($field);
4098     }
4099
4100     # FIXME: should we be making sure the biblionumbers are the same?
4101     my $newbiblionumber =
4102       &ModBiblioMarc( $newrec, $biblionumber, $frameworkcode );
4103     return $newbiblionumber;
4104 }
4105
4106 =head2 z3950_extended_services
4107
4108 z3950_extended_services($serviceType,$serviceOptions,$record);
4109
4110     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.
4111
4112 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
4113
4114 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
4115
4116     action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
4117
4118 and maybe
4119
4120     recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
4121     syntax => the record syntax (transfer syntax)
4122     databaseName = Database from connection object
4123
4124     To set serviceOptions, call set_service_options($serviceType)
4125
4126 C<$record> the record, if one is needed for the service type
4127
4128     A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
4129
4130 =cut
4131
4132 sub z3950_extended_services {
4133     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
4134
4135     # get our connection object
4136     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
4137
4138     # create a new package object
4139     my $Zpackage = $Zconn->package();
4140
4141     # set our options
4142     $Zpackage->option( action => $action );
4143
4144     if ( $serviceOptions->{'databaseName'} ) {
4145         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
4146     }
4147     if ( $serviceOptions->{'recordIdNumber'} ) {
4148         $Zpackage->option(
4149             recordIdNumber => $serviceOptions->{'recordIdNumber'} );
4150     }
4151     if ( $serviceOptions->{'recordIdOpaque'} ) {
4152         $Zpackage->option(
4153             recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
4154     }
4155
4156  # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
4157  #if ($serviceType eq 'itemorder') {
4158  #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
4159  #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
4160  #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
4161  #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
4162  #}
4163
4164     if ( $serviceOptions->{record} ) {
4165         $Zpackage->option( record => $serviceOptions->{record} );
4166
4167         # can be xml or marc
4168         if ( $serviceOptions->{'syntax'} ) {
4169             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
4170         }
4171     }
4172
4173     # send the request, handle any exception encountered
4174     eval { $Zpackage->send($serviceType) };
4175     if ( $@ && $@->isa("ZOOM::Exception") ) {
4176         return "error:  " . $@->code() . " " . $@->message() . "\n";
4177     }
4178
4179     # free up package resources
4180     $Zpackage->destroy();
4181 }
4182
4183 =head2 set_service_options
4184
4185 my $serviceOptions = set_service_options($serviceType);
4186
4187 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
4188
4189 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
4190
4191 =cut
4192
4193 sub set_service_options {
4194     my ($serviceType) = @_;
4195     my $serviceOptions;
4196
4197 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
4198 #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
4199
4200     if ( $serviceType eq 'commit' ) {
4201
4202         # nothing to do
4203     }
4204     if ( $serviceType eq 'create' ) {
4205
4206         # nothing to do
4207     }
4208     if ( $serviceType eq 'drop' ) {
4209         die "ERROR: 'drop' not currently supported (by Zebra)";
4210     }
4211     return $serviceOptions;
4212 }
4213
4214 =head2 GetItemsCount
4215
4216 $count = &GetItemsCount( $biblionumber);
4217 this function return count of item with $biblionumber
4218 =cut
4219
4220 sub GetItemsCount {
4221     my ( $biblionumber ) = @_;
4222     my $dbh = C4::Context->dbh;
4223     my $query = "SELECT count(*)
4224                   FROM  items 
4225                   WHERE biblionumber=?";
4226     my $sth = $dbh->prepare($query);
4227     $sth->execute($biblionumber);
4228     my $count = $sth->fetchrow;  
4229     $sth->finish;
4230     return ($count);
4231 }
4232
4233 END { }    # module clean-up code here (global destructor)
4234
4235 1;
4236
4237 __END__
4238
4239 =head1 AUTHOR
4240
4241 Koha Developement team <info@koha.org>
4242
4243 Paul POULAIN paul.poulain@free.fr
4244
4245 Joshua Ferraro jmf@liblime.com
4246
4247 =cut