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