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