leader must be 25 and not 24 char long
[koha.git] / C4 / Biblio.pm
1 package C4::Biblio;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 use strict;
21
22 require Exporter;
23 # use utf8;
24 use C4::Context;
25 use MARC::Record;
26 use MARC::File::USMARC;
27 use MARC::File::XML;
28 use ZOOM;
29 use C4::Koha;
30 use C4::Dates qw/format_date/;
31 use C4::Log; # logaction
32 use C4::ClassSource;
33
34 use vars qw($VERSION @ISA @EXPORT);
35
36 # TODO: fix version
37 # $VERSION = ?;
38
39 @ISA = qw( Exporter );
40
41 # EXPORTED FUNCTIONS.
42
43 # to add biblios or items
44 push @EXPORT, qw( &AddBiblio &AddItem );
45
46 # to get something
47 push @EXPORT, qw(
48   &GetBiblio
49   &GetBiblioData
50   &GetBiblioItemData
51   &GetBiblioItemInfosOf
52   &GetBiblioItemByBiblioNumber
53   &GetBiblioFromItemNumber
54   
55   &GetMarcItem
56   &GetItem
57   &GetItemInfosOf
58   &GetItemStatus
59   &GetItemLocation
60   &GetLostItems
61   &GetItemsForInventory
62   &GetItemsCount
63
64   &GetMarcNotes
65   &GetMarcSubjects
66   &GetMarcBiblio
67   &GetMarcAuthors
68   &GetMarcSeries
69   GetMarcUrls
70   &GetUsedMarcStructure
71
72   &GetItemsInfo
73   &GetItemsByBiblioitemnumber
74   &GetItemnumberFromBarcode
75   &get_itemnumbers_of
76   &GetXmlBiblio
77
78   &GetAuthorisedValueDesc
79   &GetMarcStructure
80   &GetMarcFromKohaField
81   &GetFrameworkCode
82   &GetPublisherNameFromIsbn
83   &TransformKohaToMarc
84 );
85
86 # To modify something
87 push @EXPORT, qw(
88   &ModBiblio
89   &ModItem
90   &ModItemTransfer
91   &ModBiblioframework
92   &ModZebra
93   &ModItemInMarc
94   &ModItemInMarconefield
95   &ModDateLastSeen
96 );
97
98 # To delete something
99 push @EXPORT, qw(
100   &DelBiblio
101   &DelItem
102 );
103
104 # Internal functions
105 # those functions are exported but should not be used
106 # they are usefull is few circumstances, so are exported.
107 # but don't use them unless you're a core developer ;-)
108 push @EXPORT, qw(
109   &ModBiblioMarc
110   &AddItemInMarc
111 );
112
113 # Others functions
114 push @EXPORT, qw(
115   &TransformMarcToKoha
116   &TransformHtmlToMarc2
117   &TransformHtmlToMarc
118   &TransformHtmlToXml
119   &PrepareItemrecordDisplay
120   &char_decode
121   &GetNoZebraIndexes
122 );
123
124 =head1 NAME
125
126 C4::Biblio - cataloging management functions
127
128 =head1 DESCRIPTION
129
130 Biblio.pm contains functions for managing storage and editing of bibliographic data within Koha. Most of the functions in this module are used for cataloging records: adding, editing, or removing biblios, biblioitems, or items. Koha's stores bibliographic information in three places:
131
132 =over 4
133
134 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
135
136 =item 2. as raw MARC in the Zebra index and storage engine
137
138 =item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
139
140 =back
141
142 In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
143
144 Because the data isn't completely normalized there's a chance for information to get out of sync. The design choice to go with a un-normalized schema was driven by performance and stability concerns. However, if this occur, it can be considered as a bug : The API is (or should be) complete & the only entry point for all biblio/items managements.
145
146 =over 4
147
148 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
149
150 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
151
152 =back
153
154 Because of this design choice, the process of managing storage and editing is a bit convoluted. Historically, Biblio.pm's grown to an unmanagable size and as a result we have several types of functions currently:
155
156 =over 4
157
158 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
159
160 =item 2. _koha_* - low-level internal functions for managing the koha tables
161
162 =item 3. Marc management function : as the MARC record is stored in biblioitems.marc(xml), some subs dedicated to it's management are in this package. They should be used only internally by Biblio.pm, the only official entry points being AddBiblio, AddItem, ModBiblio, ModItem.
163
164 =item 4. Zebra functions used to update the Zebra index
165
166 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
167
168 =back
169
170 The MARC record (in biblioitems.marcxml) contains the complete marc record, including items. It also contains the biblionumber. That is the reason why it is not stored directly by AddBiblio, with all other fields . To save a biblio, we need to :
171
172 =over 4
173
174 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
175
176 =item 2. add the biblionumber and biblioitemnumber into the MARC records
177
178 =item 3. save the marc record
179
180 =back
181
182 When dealing with items, we must :
183
184 =over 4
185
186 =item 1. save the item in items table, that gives us an itemnumber
187
188 =item 2. add the itemnumber to the item MARC field
189
190 =item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
191
192 When modifying a biblio or an item, the behaviour is quite similar.
193
194 =back
195
196 =head1 EXPORTED FUNCTIONS
197
198 =head2 AddBiblio
199
200 =over 4
201
202 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
203 Exported function (core API) for adding a new biblio to koha.
204
205 =back
206
207 =cut
208
209 sub AddBiblio {
210     my ( $record, $frameworkcode ) = @_;
211         my ($biblionumber,$biblioitemnumber,$error);
212     my $dbh = C4::Context->dbh;
213     # transform the data into koha-table style data
214     my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
215     ($biblionumber,$error) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
216     $olddata->{'biblionumber'} = $biblionumber;
217     ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $olddata );
218
219     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
220
221     # now add the record
222     $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
223       
224     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio") 
225         if C4::Context->preference("CataloguingLog");
226
227     return ( $biblionumber, $biblioitemnumber );
228 }
229
230 =head2 AddItem
231
232 =over 2
233
234     $biblionumber = AddItem( $record, $biblionumber)
235     Exported function (core API) for adding a new item to Koha
236
237 =back
238
239 =cut
240
241 sub AddItem {
242     my ( $record, $biblionumber ) = @_;
243     my $dbh = C4::Context->dbh;
244     
245     # add item in old-DB
246     my $frameworkcode = GetFrameworkCode( $biblionumber );
247     my $item = &TransformMarcToKoha( $dbh, $record, $frameworkcode );
248
249     # needs old biblionumber and biblioitemnumber
250     $item->{'biblionumber'} = $biblionumber;
251     my $sth =
252       $dbh->prepare(
253         "SELECT biblioitemnumber,itemtype FROM biblioitems WHERE biblionumber=?"
254       );
255     $sth->execute( $item->{'biblionumber'} );
256     my $itemtype;
257     ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow;
258     $sth =
259       $dbh->prepare(
260         "SELECT notforloan FROM itemtypes WHERE itemtype='$itemtype'");
261     $sth->execute();
262     my $notforloan = $sth->fetchrow;
263     ##Change the notforloan field if $notforloan found
264     if ( $notforloan > 0 ) {
265         $item->{'notforloan'} = $notforloan;
266         &MARCitemchange( $record, "items.notforloan", $notforloan );
267     }
268     if ( !$item->{'dateaccessioned'} || $item->{'dateaccessioned'} eq '' ) {
269
270         # find today's date
271         my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
272           localtime(time);
273         $year += 1900;
274         $mon  += 1;
275         my $date =
276           "$year-" . sprintf( "%0.2d", $mon ) . "-" . sprintf( "%0.2d", $mday );
277         $item->{'dateaccessioned'} = $date;
278         &MARCitemchange( $record, "items.dateaccessioned", $date );
279     }
280     my ( $itemnumber, $error ) = &_koha_new_items( $dbh, $item, $item->{barcode} );
281     # add itemnumber to MARC::Record before adding the item.
282     $sth = $dbh->prepare(
283 "SELECT tagfield,tagsubfield 
284 FROM marc_subfield_structure
285 WHERE frameworkcode=? 
286         AND kohafield=?"
287       );
288     &TransformKohaToMarcOneField( $sth, $record, "items.itemnumber", $itemnumber,
289         $frameworkcode );
290
291     # add the item
292     &AddItemInMarc( $record, $item->{'biblionumber'},$frameworkcode );
293    
294     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") 
295         if C4::Context->preference("CataloguingLog");
296     
297     return ($item->{biblionumber}, $item->{biblioitemnumber},$itemnumber);
298 }
299
300 =head2 ModBiblio
301
302     ModBiblio( $record,$biblionumber,$frameworkcode);
303     Exported function (core API) to modify a biblio
304
305 =cut
306
307 sub ModBiblio {
308     my ( $record, $biblionumber, $frameworkcode ) = @_;
309     if (C4::Context->preference("CataloguingLog")) {
310         my $newrecord = GetMarcBiblio($biblionumber);
311         &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,"BEFORE=>".$newrecord->as_formatted);
312     }
313     
314     my $dbh = C4::Context->dbh;
315     
316     $frameworkcode = "" unless $frameworkcode;
317
318     # get the items before and append them to the biblio before updating the record, atm we just have the biblio
319     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
320     my $oldRecord = GetMarcBiblio( $biblionumber );
321     
322     # parse each item, and, for an unknown reason, re-encode each subfield 
323     # if you don't do that, the record will have encoding mixed
324     # and the biblio will be re-encoded.
325     # strange, I (Paul P.) searched more than 1 day to understand what happends
326     # but could only solve the problem this way...
327    my @fields = $oldRecord->field( $itemtag );
328     foreach my $fielditem ( @fields ){
329         my $field;
330         foreach ($fielditem->subfields()) {
331             if ($field) {
332                 $field->add_subfields(Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
333             } else {
334                 $field = MARC::Field->new("$itemtag",'','',Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
335             }
336           }
337         $record->append_fields($field);
338     }
339     
340     # update biblionumber and biblioitemnumber in MARC
341     # FIXME - this is assuming a 1 to 1 relationship between
342     # biblios and biblioitems
343     my $sth =  $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
344     $sth->execute($biblionumber);
345     my ($biblioitemnumber) = $sth->fetchrow;
346     $sth->finish();
347     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
348
349     # update the MARC record (that now contains biblio and items) with the new record data
350     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
351     
352     # load the koha-table data object
353     my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
354
355     # modify the other koha tables
356     _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
357     _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
358     return 1;
359 }
360
361 =head2 ModItem
362
363 =over 2
364
365 Exported function (core API) for modifying an item in Koha.
366
367 =back
368
369 =cut
370
371 sub ModItem {
372     my ( $record, $biblionumber, $itemnumber, $delete, $new_item_hashref )
373       = @_;
374     
375     #logging
376     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$record->as_formatted) 
377         if C4::Context->preference("CataloguingLog");
378       
379     my $dbh = C4::Context->dbh;
380     
381     # if we have a MARC record, we're coming from cataloging and so
382     # we do the whole routine: update the MARC and zebra, then update the koha
383     # tables
384     if ($record) {
385         my $frameworkcode = GetFrameworkCode( $biblionumber );
386         ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
387         my $olditem       = TransformMarcToKoha( $dbh, $record, $frameworkcode,'items');
388         $olditem->{'biblionumber'} = $biblionumber;
389         my $sth =  $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
390         $sth->execute($biblionumber);
391         my ($biblioitemnumber) = $sth->fetchrow;
392         $sth->finish(); 
393         $olditem->{'biblioitemnumber'} = $biblioitemnumber;
394         _koha_modify_item( $dbh, $olditem );
395         return $biblionumber;
396     }
397
398     # otherwise, we're just looking to modify something quickly
399     # (like a status) so we just update the koha tables
400     elsif ($new_item_hashref) {
401         _koha_modify_item( $dbh, $new_item_hashref );
402     }
403 }
404
405 sub ModItemTransfer {
406     my ( $itemnumber, $frombranch, $tobranch ) = @_;
407     
408     my $dbh = C4::Context->dbh;
409     
410     #new entry in branchtransfers....
411     my $sth = $dbh->prepare(
412         "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
413         VALUES (?, ?, NOW(), ?)");
414     $sth->execute($itemnumber, $frombranch, $tobranch);
415     #update holdingbranch in items .....
416      $sth= $dbh->prepare(
417           "UPDATE items SET holdingbranch = ? WHERE items.itemnumber = ?");
418     $sth->execute($tobranch,$itemnumber);
419     &ModDateLastSeen($itemnumber);
420     $sth = $dbh->prepare(
421         "SELECT biblionumber FROM items WHERE itemnumber=?"
422       );
423     $sth->execute($itemnumber);
424     while ( my ( $biblionumber ) = $sth->fetchrow ) {
425         &ModItemInMarconefield( $biblionumber, $itemnumber,
426             'items.holdingbranch', $tobranch );
427     }
428     return;
429 }
430
431 =head2 ModBiblioframework
432
433     ModBiblioframework($biblionumber,$frameworkcode);
434     Exported function to modify a biblio framework
435
436 =cut
437
438 sub ModBiblioframework {
439     my ( $biblionumber, $frameworkcode ) = @_;
440     my $dbh = C4::Context->dbh;
441     my $sth = $dbh->prepare(
442         "UPDATE biblio SET frameworkcode=? WHERE biblionumber=?"
443     );
444     $sth->execute($frameworkcode, $biblionumber);
445     return 1;
446 }
447
448 =head2 ModItemInMarconefield
449
450 =over
451
452 modify only 1 field in a MARC item (mainly used for holdingbranch, but could also be used for status modif - moving a book to "lost" on a long overdu for example)
453 &ModItemInMarconefield( $biblionumber, $itemnumber, $itemfield, $newvalue )
454
455 =back
456
457 =cut
458
459 sub ModItemInMarconefield {
460     my ( $biblionumber, $itemnumber, $itemfield, $newvalue ) = @_;
461     my $dbh = C4::Context->dbh;
462     if ( !defined $newvalue ) {
463         $newvalue = "";
464     }
465
466     my $record = GetMarcItem( $biblionumber, $itemnumber );
467     my ($tagfield, $tagsubfield) = GetMarcFromKohaField( $itemfield,'');
468     if ($tagfield && $tagsubfield) {
469         my $tag = $record->field($tagfield);
470         if ($tag) {
471 #             my $tagsubs = $record->field($tagfield)->subfield($tagsubfield);
472             $tag->update( $tagsubfield => $newvalue );
473             $record->delete_field($tag);
474             $record->insert_fields_ordered($tag);
475             &ModItemInMarc( $record, $biblionumber, $itemnumber, 0 );
476         }
477     }
478 }
479
480 =head2 ModItemInMarc
481
482 =over
483
484 &ModItemInMarc( $record, $biblionumber, $itemnumber )
485
486 =back
487
488 =cut
489
490 sub ModItemInMarc {
491     my ( $ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
492     my $dbh = C4::Context->dbh;
493     
494     # get complete MARC record & replace the item field by the new one
495     my $completeRecord = GetMarcBiblio($biblionumber);
496     my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
497     my $itemField = $ItemRecord->field($itemtag);
498     my @items = $completeRecord->field($itemtag);
499     foreach (@items) {
500         if ($_->subfield($itemsubfield) eq $itemnumber) {
501 #             $completeRecord->delete_field($_);
502             $_->replace_with($itemField);
503         }
504     }
505     # save the record
506     my $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
507     $sth->execute( $completeRecord->as_usmarc(), $completeRecord->as_xml_record(),$biblionumber );
508     $sth->finish;
509     ModZebra($biblionumber,"specialUpdate","biblioserver",$completeRecord);
510 }
511
512 =head2 ModDateLastSeen
513
514 &ModDateLastSeen($itemnum)
515 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking
516 C<$itemnum> is the item number
517
518 =cut
519
520 sub ModDateLastSeen {
521     my ($itemnum) = @_;
522     my $dbh       = C4::Context->dbh;
523     my $sth       =
524       $dbh->prepare(
525           "UPDATE items SET itemlost=0,datelastseen  = NOW() WHERE items.itemnumber = ?"
526       );
527     $sth->execute($itemnum);
528     return;
529 }
530 =head2 DelBiblio
531
532 =over
533
534 my $error = &DelBiblio($dbh,$biblionumber);
535 Exported function (core API) for deleting a biblio in koha.
536 Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)
537 Also backs it up to deleted* tables
538 Checks to make sure there are not issues on any of the items
539 return:
540 C<$error> : undef unless an error occurs
541
542 =back
543
544 =cut
545
546 sub DelBiblio {
547     my ( $biblionumber ) = @_;
548     my $dbh = C4::Context->dbh;
549     my $error;    # for error handling
550         
551         # First make sure this biblio has no items attached
552         my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
553         $sth->execute($biblionumber);
554         if (my $itemnumber = $sth->fetchrow){
555                 # Fix this to use a status the template can understand
556                 $error .= "This Biblio has items attached, please delete them first before deleting this biblio ";
557         }
558
559     return $error if $error;
560
561     # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
562     # for at least 2 reasons :
563     # - we need to read the biblio if NoZebra is set (to remove it from the indexes
564     # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
565     #   and we would have no way to remove it (except manually in zebra, but I bet it would be very hard to handle the problem)
566     ModZebra($biblionumber, "delete_record", "biblioserver", undef);
567
568     # delete 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 = "
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     ";
663     my $sth = $dbh->prepare($query);
664     $sth->execute($bibnum);
665     my $data;
666     $data = $sth->fetchrow_hashref;
667     $sth->finish;
668
669     return ($data);
670 }    # sub GetBiblioData
671
672
673 =head2 GetItemsInfo
674
675 =over 4
676
677   @results = &GetItemsInfo($biblionumber, $type);
678
679 Returns information about books with the given biblionumber.
680
681 C<$type> may be either C<intra> or anything else. If it is not set to
682 C<intra>, then the search will exclude lost, very overdue, and
683 withdrawn items.
684
685 C<&GetItemsInfo> returns a list of references-to-hash. Each element
686 contains a number of keys. Most of them are table items from the
687 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
688 Koha database. Other keys include:
689
690 =over 4
691
692 =item C<$data-E<gt>{branchname}>
693
694 The name (not the code) of the branch to which the book belongs.
695
696 =item C<$data-E<gt>{datelastseen}>
697
698 This is simply C<items.datelastseen>, except that while the date is
699 stored in YYYY-MM-DD format in the database, here it is converted to
700 DD/MM/YYYY format. A NULL date is returned as C<//>.
701
702 =item C<$data-E<gt>{datedue}>
703
704 =item C<$data-E<gt>{class}>
705
706 This is the concatenation of C<biblioitems.classification>, the book's
707 Dewey code, and C<biblioitems.subclass>.
708
709 =item C<$data-E<gt>{ocount}>
710
711 I think this is the number of copies of the book available.
712
713 =item C<$data-E<gt>{order}>
714
715 If this is set, it is set to C<One Order>.
716
717 =back
718
719 =back
720
721 =cut
722
723 sub GetItemsInfo {
724     my ( $biblionumber, $type ) = @_;
725     my $dbh   = C4::Context->dbh;
726     my $query = "SELECT *,items.notforloan as itemnotforloan
727                  FROM items 
728                  LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
729                  LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
730                  LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype
731                 WHERE items.biblionumber = ?
732                 ORDER BY items.dateaccessioned desc
733                  ";
734     my $sth = $dbh->prepare($query);
735     $sth->execute($biblionumber);
736     my $i = 0;
737     my @results;
738     my ( $date_due, $count_reserves );
739
740     while ( my $data = $sth->fetchrow_hashref ) {
741         my $datedue = '';
742         my $isth    = $dbh->prepare(
743             "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname
744             FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
745             WHERE  itemnumber = ?
746                 AND returndate IS NULL"
747         );
748         $isth->execute( $data->{'itemnumber'} );
749         if ( my $idata = $isth->fetchrow_hashref ) {
750             $data->{borrowernumber} = $idata->{borrowernumber};
751             $data->{cardnumber}     = $idata->{cardnumber};
752             $data->{surname}     = $idata->{surname};
753             $data->{firstname}     = $idata->{firstname};
754             $datedue                = format_date( $idata->{'date_due'} );
755         }
756         if ( $datedue eq '' ) {
757             #$datedue="Available";
758             my ( $restype, $reserves ) =
759               C4::Reserves::CheckReserves( $data->{'itemnumber'} );
760             if ($restype) {
761
762                 #$datedue=$restype;
763                 $count_reserves = $restype;
764             }
765         }
766         $isth->finish;
767
768         #get branch information.....
769         my $bsth = $dbh->prepare(
770             "SELECT * FROM branches WHERE branchcode = ?
771         "
772         );
773         $bsth->execute( $data->{'holdingbranch'} );
774         if ( my $bdata = $bsth->fetchrow_hashref ) {
775             $data->{'branchname'} = $bdata->{'branchname'};
776         }
777         my $date = format_date( $data->{'datelastseen'} );
778         $data->{'datelastseen'}   = $date;
779         $data->{'datedue'}        = $datedue;
780         $data->{'count_reserves'} = $count_reserves;
781
782         # get notforloan complete status if applicable
783         my $sthnflstatus = $dbh->prepare(
784             'SELECT authorised_value
785             FROM   marc_subfield_structure
786             WHERE  kohafield="items.notforloan"
787         '
788         );
789
790         $sthnflstatus->execute;
791         my ($authorised_valuecode) = $sthnflstatus->fetchrow;
792         if ($authorised_valuecode) {
793             $sthnflstatus = $dbh->prepare(
794                 "SELECT lib FROM authorised_values
795                  WHERE  category=?
796                  AND authorised_value=?"
797             );
798             $sthnflstatus->execute( $authorised_valuecode,
799                 $data->{itemnotforloan} );
800             my ($lib) = $sthnflstatus->fetchrow;
801             $data->{notforloan} = $lib;
802         }
803
804         # my stack procedures
805         my $stackstatus = $dbh->prepare(
806             'SELECT authorised_value
807              FROM   marc_subfield_structure
808              WHERE  kohafield="items.stack"
809         '
810         );
811         $stackstatus->execute;
812
813         ($authorised_valuecode) = $stackstatus->fetchrow;
814         if ($authorised_valuecode) {
815             $stackstatus = $dbh->prepare(
816                 "SELECT lib
817                  FROM   authorised_values
818                  WHERE  category=?
819                  AND    authorised_value=?
820             "
821             );
822             $stackstatus->execute( $authorised_valuecode, $data->{stack} );
823             my ($lib) = $stackstatus->fetchrow;
824             $data->{stack} = $lib;
825         }
826         $results[$i] = $data;
827         $i++;
828     }
829     $sth->finish;
830
831     return (@results);
832 }
833
834 =head2 getitemstatus
835
836 =over 4
837
838 $itemstatushash = &getitemstatus($fwkcode);
839 returns information about status.
840 Can be MARC dependant.
841 fwkcode is optional.
842 But basically could be can be loan or not
843 Create a status selector with the following code
844
845 =head3 in PERL SCRIPT
846
847 my $itemstatushash = getitemstatus;
848 my @itemstatusloop;
849 foreach my $thisstatus (keys %$itemstatushash) {
850     my %row =(value => $thisstatus,
851                 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
852             );
853     push @itemstatusloop, \%row;
854 }
855 $template->param(statusloop=>\@itemstatusloop);
856
857
858 =head3 in TEMPLATE
859
860             <select name="statusloop">
861                 <option value="">Default</option>
862             <!-- TMPL_LOOP name="statusloop" -->
863                 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
864             <!-- /TMPL_LOOP -->
865             </select>
866
867 =cut
868
869 sub GetItemStatus {
870
871     # returns a reference to a hash of references to status...
872     my ($fwk) = @_;
873     my %itemstatus;
874     my $dbh = C4::Context->dbh;
875     my $sth;
876     $fwk = '' unless ($fwk);
877     my ( $tag, $subfield ) =
878       GetMarcFromKohaField( "items.notforloan", $fwk );
879     if ( $tag and $subfield ) {
880         my $sth =
881           $dbh->prepare(
882                         "SELECT authorised_value
883                         FROM marc_subfield_structure
884                         WHERE tagfield=?
885                                 AND tagsubfield=?
886                                 AND frameworkcode=?
887                         "
888           );
889         $sth->execute( $tag, $subfield, $fwk );
890         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
891             my $authvalsth =
892               $dbh->prepare(
893                                 "SELECT authorised_value,lib
894                                 FROM authorised_values 
895                                 WHERE category=? 
896                                 ORDER BY lib
897                                 "
898               );
899             $authvalsth->execute($authorisedvaluecat);
900             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
901                 $itemstatus{$authorisedvalue} = $lib;
902             }
903             $authvalsth->finish;
904             return \%itemstatus;
905             exit 1;
906         }
907         else {
908
909             #No authvalue list
910             # build default
911         }
912         $sth->finish;
913     }
914
915     #No authvalue list
916     #build default
917     $itemstatus{"1"} = "Not For Loan";
918     return \%itemstatus;
919 }
920
921 =head2 getitemlocation
922
923 =over 4
924
925 $itemlochash = &getitemlocation($fwk);
926 returns informations about location.
927 where fwk stands for an optional framework code.
928 Create a location selector with the following code
929
930 =head3 in PERL SCRIPT
931
932 my $itemlochash = getitemlocation;
933 my @itemlocloop;
934 foreach my $thisloc (keys %$itemlochash) {
935     my $selected = 1 if $thisbranch eq $branch;
936     my %row =(locval => $thisloc,
937                 selected => $selected,
938                 locname => $itemlochash->{$thisloc},
939             );
940     push @itemlocloop, \%row;
941 }
942 $template->param(itemlocationloop => \@itemlocloop);
943
944 =head3 in TEMPLATE
945
946 <select name="location">
947     <option value="">Default</option>
948 <!-- TMPL_LOOP name="itemlocationloop" -->
949     <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
950 <!-- /TMPL_LOOP -->
951 </select>
952
953 =back
954
955 =cut
956
957 sub GetItemLocation {
958
959     # returns a reference to a hash of references to location...
960     my ($fwk) = @_;
961     my %itemlocation;
962     my $dbh = C4::Context->dbh;
963     my $sth;
964     $fwk = '' unless ($fwk);
965     my ( $tag, $subfield ) =
966       GetMarcFromKohaField( "items.location", $fwk );
967     if ( $tag and $subfield ) {
968         my $sth =
969           $dbh->prepare(
970                         "SELECT authorised_value
971                         FROM marc_subfield_structure 
972                         WHERE tagfield=? 
973                                 AND tagsubfield=? 
974                                 AND frameworkcode=?"
975           );
976         $sth->execute( $tag, $subfield, $fwk );
977         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
978             my $authvalsth =
979               $dbh->prepare(
980                                 "SELECT authorised_value,lib
981                                 FROM authorised_values
982                                 WHERE category=?
983                                 ORDER BY lib"
984               );
985             $authvalsth->execute($authorisedvaluecat);
986             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
987                 $itemlocation{$authorisedvalue} = $lib;
988             }
989             $authvalsth->finish;
990             return \%itemlocation;
991             exit 1;
992         }
993         else {
994
995             #No authvalue list
996             # build default
997         }
998         $sth->finish;
999     }
1000
1001     #No authvalue list
1002     #build default
1003     $itemlocation{"1"} = "Not For Loan";
1004     return \%itemlocation;
1005 }
1006
1007 =head2 GetLostItems
1008
1009 $items = GetLostItems($where,$orderby);
1010
1011 This function get the items lost into C<$items>.
1012
1013 =over 2
1014
1015 =item input:
1016 C<$where> is a hashref. it containts a field of the items table as key
1017 and the value to match as value.
1018 C<$orderby> is a field of the items table.
1019
1020 =item return:
1021 C<$items> is a reference to an array full of hasref which keys are items' table column.
1022
1023 =item usage in the perl script:
1024
1025 my %where;
1026 $where{barcode} = 0001548;
1027 my $items = GetLostItems( \%where, "homebranch" );
1028 $template->param(itemsloop => $items);
1029
1030 =back
1031
1032 =cut
1033
1034 sub GetLostItems {
1035     # Getting input args.
1036     my $where   = shift;
1037     my $orderby = shift;
1038     my $dbh     = C4::Context->dbh;
1039
1040     my $query   = "
1041         SELECT *
1042         FROM   items
1043         WHERE  itemlost IS NOT NULL
1044           AND  itemlost <> 0
1045     ";
1046     foreach my $key (keys %$where) {
1047         $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
1048     }
1049     $query .= " ORDER BY ".$orderby if defined $orderby;
1050
1051     my $sth = $dbh->prepare($query);
1052     $sth->execute;
1053     my @items;
1054     while ( my $row = $sth->fetchrow_hashref ){
1055         push @items, $row;
1056     }
1057     return \@items;
1058 }
1059
1060 =head2 GetItemsForInventory
1061
1062 $itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
1063
1064 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1065
1066 The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
1067 It is ordered by callnumber,title.
1068
1069 The minlocation & maxlocation parameters are used to specify a range of item callnumbers
1070 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1071 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1072
1073 =cut
1074
1075 sub GetItemsForInventory {
1076     my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
1077     my $dbh = C4::Context->dbh;
1078     my $sth;
1079     if ($datelastseen) {
1080         $datelastseen=format_date_in_iso($datelastseen);  
1081         my $query =
1082                 "SELECT itemnumber,barcode,itemcallnumber,title,author,biblio.biblionumber,datelastseen
1083                  FROM items
1084                    LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
1085                  WHERE itemcallnumber>= ?
1086                    AND itemcallnumber <=?
1087                    AND (datelastseen< ? OR datelastseen IS NULL)";
1088         $query.= " AND items.location=".$dbh->quote($location) if $location;
1089         $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1090         $query .= " ORDER BY itemcallnumber,title";
1091         $sth = $dbh->prepare($query);
1092         $sth->execute( $minlocation, $maxlocation, $datelastseen );
1093     }
1094     else {
1095         my $query ="
1096                 SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen
1097                 FROM items 
1098                   LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
1099                 WHERE itemcallnumber>= ?
1100                   AND itemcallnumber <=?";
1101         $query.= " AND items.location=".$dbh->quote($location) if $location;
1102         $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
1103         $query .= " ORDER BY itemcallnumber,title";
1104         $sth = $dbh->prepare($query);
1105         $sth->execute( $minlocation, $maxlocation );
1106     }
1107     my @results;
1108     while ( my $row = $sth->fetchrow_hashref ) {
1109         $offset-- if ($offset);
1110         $row->{datelastseen}=format_date($row->{datelastseen});
1111         if ( ( !$offset ) && $size ) {
1112             push @results, $row;
1113             $size--;
1114         }
1115     }
1116     return \@results;
1117 }
1118
1119 =head2 &GetBiblioItemData
1120
1121 =over 4
1122
1123 $itemdata = &GetBiblioItemData($biblioitemnumber);
1124
1125 Looks up the biblioitem with the given biblioitemnumber. Returns a
1126 reference-to-hash. The keys are the fields from the C<biblio>,
1127 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
1128 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
1129
1130 =back
1131
1132 =cut
1133
1134 #'
1135 sub GetBiblioItemData {
1136     my ($biblioitemnumber) = @_;
1137     my $dbh       = C4::Context->dbh;
1138     my $sth       =
1139       $dbh->prepare(
1140         "SELECT *,biblioitems.notes AS bnotes
1141                 FROM biblioitems,biblio,itemtypes 
1142         WHERE biblio.biblionumber = biblioitems.biblionumber 
1143                 AND biblioitemnumber = ? "
1144       );
1145     my $data;
1146     $sth->execute($biblioitemnumber);
1147     $data = $sth->fetchrow_hashref;
1148     $sth->finish;
1149     return ($data);
1150 }    # sub &GetBiblioItemData
1151
1152 =head2 GetItemnumberFromBarcode
1153
1154 =over 4
1155
1156 $result = GetItemnumberFromBarcode($barcode);
1157
1158 =back
1159
1160 =cut
1161
1162 sub GetItemnumberFromBarcode {
1163     my ($barcode) = @_;
1164     my $dbh = C4::Context->dbh;
1165
1166     my $rq =
1167       $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1168     $rq->execute($barcode);
1169     my ($result) = $rq->fetchrow;
1170     return ($result);
1171 }
1172
1173 =head2 GetBiblioItemByBiblioNumber
1174
1175 =over 4
1176
1177 NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.
1178
1179 =back
1180
1181 =cut
1182
1183 sub GetBiblioItemByBiblioNumber {
1184     my ($biblionumber) = @_;
1185     my $dbh = C4::Context->dbh;
1186     my $sth = $dbh->prepare("Select * FROM biblioitems WHERE biblionumber = ?");
1187     my $count = 0;
1188     my @results;
1189
1190     $sth->execute($biblionumber);
1191
1192     while ( my $data = $sth->fetchrow_hashref ) {
1193         push @results, $data;
1194     }
1195
1196     $sth->finish;
1197     return @results;
1198 }
1199
1200 =head2 GetBiblioFromItemNumber
1201
1202 =over 4
1203
1204 $item = &GetBiblioFromItemNumber($itemnumber);
1205
1206 Looks up the item with the given itemnumber.
1207
1208 C<&itemnodata> returns a reference-to-hash whose keys are the fields
1209 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
1210 database.
1211
1212 =back
1213
1214 =cut
1215
1216 #'
1217 sub GetBiblioFromItemNumber {
1218     my ( $itemnumber ) = @_;
1219     my $dbh = C4::Context->dbh;
1220     my $sth = $dbh->prepare(
1221         "SELECT * FROM items 
1222         LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1223         LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1224          WHERE items.itemnumber = ?"
1225     );
1226
1227     $sth->execute($itemnumber);
1228     my $data = $sth->fetchrow_hashref;
1229     $sth->finish;
1230     return ($data);
1231 }
1232
1233 =head2 GetBiblio
1234
1235 =over 4
1236
1237 ( $count, @results ) = &GetBiblio($biblionumber);
1238
1239 =back
1240
1241 =cut
1242
1243 sub GetBiblio {
1244     my ($biblionumber) = @_;
1245     my $dbh = C4::Context->dbh;
1246     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber = ?");
1247     my $count = 0;
1248     my @results;
1249     $sth->execute($biblionumber);
1250     while ( my $data = $sth->fetchrow_hashref ) {
1251         $results[$count] = $data;
1252         $count++;
1253     }    # while
1254     $sth->finish;
1255     return ( $count, @results );
1256 }    # sub GetBiblio
1257
1258 =head2 GetItem
1259
1260 =over 4
1261
1262 $data = &GetItem($itemnumber,$barcode);
1263
1264 return Item information, for a given itemnumber or barcode
1265
1266 =back
1267
1268 =cut
1269
1270 sub GetItem {
1271     my ($itemnumber,$barcode) = @_;
1272     my $dbh = C4::Context->dbh;
1273     if ($itemnumber) {
1274         my $sth = $dbh->prepare("
1275             SELECT * FROM items 
1276             WHERE itemnumber = ?");
1277         $sth->execute($itemnumber);
1278         my $data = $sth->fetchrow_hashref;
1279         return $data;
1280     } else {
1281         my $sth = $dbh->prepare("
1282             SELECT * FROM items 
1283             WHERE barcode = ?"
1284             );
1285         $sth->execute($barcode);
1286         my $data = $sth->fetchrow_hashref;
1287         return $data;
1288     }
1289 }    # sub GetItem
1290
1291 =head2 get_itemnumbers_of
1292
1293 =over 4
1294
1295 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1296
1297 Given a list of biblionumbers, return the list of corresponding itemnumbers
1298 for each biblionumber.
1299
1300 Return a reference on a hash where keys are biblionumbers and values are
1301 references on array of itemnumbers.
1302
1303 =back
1304
1305 =cut
1306
1307 sub get_itemnumbers_of {
1308     my @biblionumbers = @_;
1309
1310     my $dbh = C4::Context->dbh;
1311
1312     my $query = '
1313         SELECT itemnumber,
1314             biblionumber
1315         FROM items
1316         WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1317     ';
1318     my $sth = $dbh->prepare($query);
1319     $sth->execute(@biblionumbers);
1320
1321     my %itemnumbers_of;
1322
1323     while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1324         push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1325     }
1326
1327     return \%itemnumbers_of;
1328 }
1329
1330 =head2 GetItemInfosOf
1331
1332 =over 4
1333
1334 GetItemInfosOf(@itemnumbers);
1335
1336 =back
1337
1338 =cut
1339
1340 sub GetItemInfosOf {
1341     my @itemnumbers = @_;
1342
1343     my $query = '
1344         SELECT *
1345         FROM items
1346         WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1347     ';
1348     return get_infos_of( $query, 'itemnumber' );
1349 }
1350
1351 =head2 GetItemsByBiblioitemnumber
1352
1353 =over 4
1354
1355 GetItemsByBiblioitemnumber($biblioitemnumber);
1356
1357 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1358 Called by moredetail.pl
1359
1360 =back
1361
1362 =cut
1363
1364 sub GetItemsByBiblioitemnumber {
1365         my ( $bibitem ) = @_;
1366         my $dbh = C4::Context->dbh;
1367         my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1368         # Get all items attached to a biblioitem
1369     my $i = 0;
1370     my @results; 
1371     $sth->execute($bibitem) || die $sth->errstr;
1372     while ( my $data = $sth->fetchrow_hashref ) {  
1373                 # Foreach item, get circulation information
1374                 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1375                                    WHERE itemnumber = ?
1376                                    AND returndate is NULL
1377                                    AND issues.borrowernumber = borrowers.borrowernumber"
1378         );
1379         $sth2->execute( $data->{'itemnumber'} );
1380         if ( my $data2 = $sth2->fetchrow_hashref ) {
1381                         # if item is out, set the due date and who it is out too
1382                         $data->{'date_due'}   = $data2->{'date_due'};
1383                         $data->{'cardnumber'} = $data2->{'cardnumber'};
1384                         $data->{'borrowernumber'}   = $data2->{'borrowernumber'};
1385                 }
1386         else {
1387                         # set date_due to blank, so in the template we check itemlost, and wthdrawn 
1388                         $data->{'date_due'} = '';                                                                                                         
1389                 }    # else         
1390         $sth2->finish;
1391         # Find the last 3 people who borrowed this item.                  
1392         my $query2 = "SELECT * FROM issues, borrowers WHERE itemnumber = ?
1393                       AND issues.borrowernumber = borrowers.borrowernumber
1394                       AND returndate is not NULL
1395                       ORDER BY returndate desc,timestamp desc LIMIT 3";
1396         $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1397         $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1398         my $i2 = 0;
1399         while ( my $data2 = $sth2->fetchrow_hashref ) {
1400                         $data->{"timestamp$i2"} = $data2->{'timestamp'};
1401                         $data->{"card$i2"}      = $data2->{'cardnumber'};
1402                         $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
1403                         $i2++;
1404                 }
1405         $sth2->finish;
1406         push(@results,$data);
1407     } 
1408     $sth->finish;
1409     return (\@results); 
1410 }
1411
1412
1413 =head2 GetBiblioItemInfosOf
1414
1415 =over 4
1416
1417 GetBiblioItemInfosOf(@biblioitemnumbers);
1418
1419 =back
1420
1421 =cut
1422
1423 sub GetBiblioItemInfosOf {
1424     my @biblioitemnumbers = @_;
1425
1426     my $query = '
1427         SELECT biblioitemnumber,
1428             publicationyear,
1429             itemtype
1430         FROM biblioitems
1431         WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
1432     ';
1433     return get_infos_of( $query, 'biblioitemnumber' );
1434 }
1435
1436 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
1437
1438 =head2 GetMarcStructure
1439
1440 =over 4
1441
1442 $res = GetMarcStructure($forlibrarian,$frameworkcode);
1443
1444 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
1445 $forlibrarian  :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
1446 $frameworkcode : the framework code to read
1447
1448 =back
1449
1450 =cut
1451
1452 sub GetMarcStructure {
1453     my ( $forlibrarian, $frameworkcode ) = @_;
1454     my $dbh=C4::Context->dbh;
1455     $frameworkcode = "" unless $frameworkcode;
1456     my $sth;
1457     my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';
1458
1459     # check that framework exists
1460     $sth =
1461       $dbh->prepare(
1462         "SELECT COUNT(*) FROM marc_tag_structure WHERE frameworkcode=?");
1463     $sth->execute($frameworkcode);
1464     my ($total) = $sth->fetchrow;
1465     $frameworkcode = "" unless ( $total > 0 );
1466     $sth =
1467       $dbh->prepare(
1468                 "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable 
1469                 FROM marc_tag_structure 
1470                 WHERE frameworkcode=? 
1471                 ORDER BY tagfield"
1472       );
1473     $sth->execute($frameworkcode);
1474     my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
1475
1476     while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) =
1477         $sth->fetchrow )
1478     {
1479         $res->{$tag}->{lib} =
1480           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1481         $res->{$tab}->{tab}        = "";
1482         $res->{$tag}->{mandatory}  = $mandatory;
1483         $res->{$tag}->{repeatable} = $repeatable;
1484     }
1485
1486     $sth =
1487       $dbh->prepare(
1488                         "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue 
1489                                 FROM marc_subfield_structure 
1490                         WHERE frameworkcode=? 
1491                                 ORDER BY tagfield,tagsubfield
1492                         "
1493     );
1494     
1495     $sth->execute($frameworkcode);
1496
1497     my $subfield;
1498     my $authorised_value;
1499     my $authtypecode;
1500     my $value_builder;
1501     my $kohafield;
1502     my $seealso;
1503     my $hidden;
1504     my $isurl;
1505     my $link;
1506     my $defaultvalue;
1507
1508     while (
1509         (
1510             $tag,          $subfield,      $liblibrarian,
1511             ,              $libopac,       $tab,
1512             $mandatory,    $repeatable,    $authorised_value,
1513             $authtypecode, $value_builder, $kohafield,
1514             $seealso,      $hidden,        $isurl,
1515             $link,$defaultvalue
1516         )
1517         = $sth->fetchrow
1518       )
1519     {
1520         $res->{$tag}->{$subfield}->{lib} =
1521           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1522         $res->{$tag}->{$subfield}->{tab}              = $tab;
1523         $res->{$tag}->{$subfield}->{mandatory}        = $mandatory;
1524         $res->{$tag}->{$subfield}->{repeatable}       = $repeatable;
1525         $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
1526         $res->{$tag}->{$subfield}->{authtypecode}     = $authtypecode;
1527         $res->{$tag}->{$subfield}->{value_builder}    = $value_builder;
1528         $res->{$tag}->{$subfield}->{kohafield}        = $kohafield;
1529         $res->{$tag}->{$subfield}->{seealso}          = $seealso;
1530         $res->{$tag}->{$subfield}->{hidden}           = $hidden;
1531         $res->{$tag}->{$subfield}->{isurl}            = $isurl;
1532         $res->{$tag}->{$subfield}->{'link'}           = $link;
1533         $res->{$tag}->{$subfield}->{defaultvalue}     = $defaultvalue;
1534     }
1535     return $res;
1536 }
1537
1538 =head2 GetUsedMarcStructure
1539
1540     the same function as GetMarcStructure expcet it just take field
1541     in tab 0-9. (used field)
1542     
1543     my $results = GetUsedMarcStructure($frameworkcode);
1544     
1545     L<$results> is a ref to an array which each case containts a ref
1546     to a hash which each keys is the columns from marc_subfield_structure
1547     
1548     L<$frameworkcode> is the framework code. 
1549     
1550 =cut
1551
1552 sub GetUsedMarcStructure($){
1553     my $frameworkcode = shift || '';
1554     my $dbh           = C4::Context->dbh;
1555     my $query         = qq/
1556         SELECT *
1557         FROM   marc_subfield_structure
1558         WHERE   tab > -1 
1559             AND frameworkcode = ?
1560     /;
1561     my @results;
1562     my $sth = $dbh->prepare($query);
1563     $sth->execute($frameworkcode);
1564     while (my $row = $sth->fetchrow_hashref){
1565         push @results,$row;
1566     }
1567     return \@results;
1568 }
1569
1570 =head2 GetMarcFromKohaField
1571
1572 =over 4
1573
1574 ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
1575 Returns the MARC fields & subfields mapped to the koha field 
1576 for the given frameworkcode
1577
1578 =back
1579
1580 =cut
1581
1582 sub GetMarcFromKohaField {
1583     my ( $kohafield, $frameworkcode ) = @_;
1584     return 0, 0 unless $kohafield;
1585     my $relations = C4::Context->marcfromkohafield;
1586     return (
1587         $relations->{$frameworkcode}->{$kohafield}->[0],
1588         $relations->{$frameworkcode}->{$kohafield}->[1]
1589     );
1590 }
1591
1592 =head2 GetMarcBiblio
1593
1594 =over 4
1595
1596 Returns MARC::Record of the biblionumber passed in parameter.
1597 the marc record contains both biblio & item datas
1598
1599 =back
1600
1601 =cut
1602
1603 sub GetMarcBiblio {
1604     my $biblionumber = shift;
1605     my $dbh          = C4::Context->dbh;
1606     my $sth          =
1607       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1608     $sth->execute($biblionumber);
1609      my ($marcxml) = $sth->fetchrow;
1610      MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
1611      $marcxml =~ s/\x1e//g;
1612      $marcxml =~ s/\x1f//g;
1613      $marcxml =~ s/\x1d//g;
1614      $marcxml =~ s/\x0f//g;
1615      $marcxml =~ s/\x0c//g;  
1616 #   warn $marcxml;
1617     my $record = MARC::Record->new();
1618     if ($marcxml) {
1619         $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
1620         if ($@) {warn $@;}
1621 #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
1622         return $record;
1623     } else {
1624         return undef;
1625     }
1626 }
1627
1628 =head2 GetXmlBiblio
1629
1630 =over 4
1631
1632 my $marcxml = GetXmlBiblio($biblionumber);
1633
1634 Returns biblioitems.marcxml of the biblionumber passed in parameter.
1635 The XML contains both biblio & item datas
1636
1637 =back
1638
1639 =cut
1640
1641 sub GetXmlBiblio {
1642     my ( $biblionumber ) = @_;
1643     my $dbh = C4::Context->dbh;
1644     my $sth =
1645       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1646     $sth->execute($biblionumber);
1647     my ($marcxml) = $sth->fetchrow;
1648     return $marcxml;
1649 }
1650
1651 =head2 GetAuthorisedValueDesc
1652
1653 =over 4
1654
1655 my $subfieldvalue =get_authorised_value_desc(
1656     $tag, $subf[$i][0],$subf[$i][1], '', $taglib);
1657 Retrieve the complete description for a given authorised value.
1658
1659 =back
1660
1661 =cut
1662
1663 sub GetAuthorisedValueDesc {
1664     my ( $tag, $subfield, $value, $framework, $tagslib ) = @_;
1665     my $dbh = C4::Context->dbh;
1666     
1667     #---- branch
1668     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1669         return C4::Branch::GetBranchName($value);
1670     }
1671
1672     #---- itemtypes
1673     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1674         return getitemtypeinfo($value)->{description};
1675     }
1676
1677     #---- "true" authorized value
1678     my $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1679     if ( $category ne "" ) {
1680         my $sth =
1681           $dbh->prepare(
1682             "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
1683           );
1684         $sth->execute( $category, $value );
1685         my $data = $sth->fetchrow_hashref;
1686         return $data->{'lib'};
1687     }
1688     else {
1689         return $value;    # if nothing is found return the original value
1690     }
1691 }
1692
1693 =head2 GetMarcItem
1694
1695 =over 4
1696
1697 Returns MARC::Record of the item passed in parameter.
1698
1699 =back
1700
1701 =cut
1702
1703 sub GetMarcItem {
1704     my ( $biblionumber, $itemnumber ) = @_;
1705     my $dbh = C4::Context->dbh;
1706     my $newrecord = MARC::Record->new();
1707     my $marcflavour = C4::Context->preference('marcflavour');
1708     
1709     my $marcxml = GetXmlBiblio($biblionumber);
1710     my $record = MARC::Record->new();
1711     $record = MARC::Record::new_from_xml( $marcxml, "utf8", $marcflavour );
1712     # now, find where the itemnumber is stored & extract only the item
1713     my ( $itemnumberfield, $itemnumbersubfield ) =
1714       GetMarcFromKohaField( 'items.itemnumber', '' );
1715     my @fields = $record->field($itemnumberfield);
1716     foreach my $field (@fields) {
1717         if ( $field->subfield($itemnumbersubfield) eq $itemnumber ) {
1718             $newrecord->insert_fields_ordered($field);
1719         }
1720     }
1721     return $newrecord;
1722 }
1723
1724
1725
1726 =head2 GetMarcNotes
1727
1728 =over 4
1729
1730 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1731 Get all notes from the MARC record and returns them in an array.
1732 The note are stored in differents places depending on MARC flavour
1733
1734 =back
1735
1736 =cut
1737
1738 sub GetMarcNotes {
1739     my ( $record, $marcflavour ) = @_;
1740     my $scope;
1741     if ( $marcflavour eq "MARC21" ) {
1742         $scope = '5..';
1743     }
1744     else {    # assume unimarc if not marc21
1745         $scope = '3..';
1746     }
1747     my @marcnotes;
1748     my $note = "";
1749     my $tag  = "";
1750     my $marcnote;
1751     foreach my $field ( $record->field($scope) ) {
1752         my $value = $field->as_string();
1753         if ( $note ne "" ) {
1754             $marcnote = { marcnote => $note, };
1755             push @marcnotes, $marcnote;
1756             $note = $value;
1757         }
1758         if ( $note ne $value ) {
1759             $note = $note . " " . $value;
1760         }
1761     }
1762
1763     if ( $note ) {
1764         $marcnote = { marcnote => $note };
1765         push @marcnotes, $marcnote;    #load last tag into array
1766     }
1767     return \@marcnotes;
1768 }    # end GetMarcNotes
1769
1770 =head2 GetMarcSubjects
1771
1772 =over 4
1773
1774 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1775 Get all subjects from the MARC record and returns them in an array.
1776 The subjects are stored in differents places depending on MARC flavour
1777
1778 =back
1779
1780 =cut
1781
1782 sub GetMarcSubjects {
1783     my ( $record, $marcflavour ) = @_;
1784     my ( $mintag, $maxtag );
1785     if ( $marcflavour eq "MARC21" ) {
1786         $mintag = "600";
1787         $maxtag = "699";
1788     }
1789     else {    # assume unimarc if not marc21
1790         $mintag = "600";
1791         $maxtag = "611";
1792     }
1793
1794     my @marcsubjcts;
1795
1796     foreach my $field ( $record->fields ) {
1797         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1798         my @subfields = $field->subfields();
1799         my $link = "su:";
1800         my $label;
1801         my $flag = 0;
1802         my $authoritysep=C4::Context->preference("authoritysep");
1803         for my $subject_subfield ( @subfields ) {
1804             if (
1805                 $marcflavour ne 'MARC21'
1806                 and (
1807                     ($subject_subfield->[0] eq '3') or
1808                     ($subject_subfield->[0] eq '4') or
1809                     ($subject_subfield->[0] eq '5')
1810                 )
1811             )
1812             {
1813                 next;
1814             }
1815             my $code = $subject_subfield->[0];
1816             $label .= $subject_subfield->[1].$authoritysep unless ( $code == 9 );
1817             $link  .= " and su-to:".$subject_subfield->[1]  unless ( $code == 9 );
1818             if ( $code == 9 ) {
1819                 $link = "an:".$subject_subfield->[1];
1820                 $flag = 1;
1821             }
1822             elsif ( ! $flag ) {
1823                 $link =~ s/ and\ssu-to:$//;
1824             }
1825         }
1826          $label =~ s/$authoritysep$//;
1827         push @marcsubjcts,
1828           {
1829             label => $label,
1830             link  => $link
1831           }
1832     }
1833     return \@marcsubjcts;
1834 }    #end GetMarcSubjects
1835
1836 =head2 GetMarcAuthors
1837
1838 =over 4
1839
1840 authors = GetMarcAuthors($record,$marcflavour);
1841 Get all authors from the MARC record and returns them in an array.
1842 The authors are stored in differents places depending on MARC flavour
1843
1844 =back
1845
1846 =cut
1847
1848 sub GetMarcAuthors {
1849     my ( $record, $marcflavour ) = @_;
1850     my ( $mintag, $maxtag );
1851     # tagslib useful for UNIMARC author reponsabilities
1852     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.
1853     if ( $marcflavour eq "MARC21" ) {
1854         $mintag = "700";
1855         $maxtag = "720"; 
1856     }
1857     elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
1858         $mintag = "701";
1859         $maxtag = "712";
1860     }
1861         else {
1862                 return;
1863         }
1864     my @marcauthors;
1865
1866     foreach my $field ( $record->fields ) {
1867         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1868         my %hash;
1869         my @subfields = $field->subfields();
1870         my $count_auth = 0;
1871         for my $authors_subfield (@subfields) {
1872                         #unimarc-specific line
1873             next if ($marcflavour eq 'UNIMARC' and (($authors_subfield->[0] eq '3') or ($authors_subfield->[0] eq '5')));
1874             my $subfieldcode = $authors_subfield->[0];
1875             my $value;
1876             # deal with UNIMARC author responsibility
1877                         if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq '4')) {
1878                 $value = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
1879             } else {
1880                 $value        = $authors_subfield->[1];
1881             }
1882             $hash{tag}       = $field->tag;
1883             $hash{value}    .= $value . " " if ($subfieldcode != 9) ;
1884             $hash{link}     .= $value if ($subfieldcode eq 9);
1885         }
1886         push @marcauthors, \%hash;
1887     }
1888     return \@marcauthors;
1889 }
1890
1891 =head2 GetMarcUrls
1892
1893 =over 4
1894
1895 $marcurls = GetMarcUrls($record,$marcflavour);
1896 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1897 Assumes web resources (not uncommon in MARC21 to omit resource type ind) 
1898
1899 =back
1900
1901 =cut
1902
1903 sub GetMarcUrls {
1904     my ($record, $marcflavour) = @_;
1905     my @marcurls;
1906     my $marcurl;
1907     for my $field ($record->field('856')) {
1908         my $url = $field->subfield('u');
1909         my @notes;
1910         for my $note ( $field->subfield('z')) {
1911             push @notes , {note => $note};
1912         }        
1913         $marcurl = {  MARCURL => $url,
1914                       notes => \@notes,
1915                                         };
1916                 if($marcflavour eq 'MARC21') {
1917                 my $s3 = $field->subfield('3');
1918                         my $link = $field->subfield('y');
1919             $marcurl->{'linktext'} = $link || $s3 || $url ;;
1920             $marcurl->{'part'} = $s3 if($link);
1921             $marcurl->{'toc'} = 1 if($s3 =~ /^[Tt]able/) ;
1922                 } else {
1923                         $marcurl->{'linktext'} = $url;
1924                 }
1925         push @marcurls, $marcurl;    
1926         }
1927     return \@marcurls;
1928 }  #end GetMarcUrls
1929
1930 =head2 GetMarcSeries
1931
1932 =over 4
1933
1934 $marcseriesarray = GetMarcSeries($record,$marcflavour);
1935 Get all series from the MARC record and returns them in an array.
1936 The series are stored in differents places depending on MARC flavour
1937
1938 =back
1939
1940 =cut
1941
1942 sub GetMarcSeries {
1943     my ($record, $marcflavour) = @_;
1944     my ($mintag, $maxtag);
1945     if ($marcflavour eq "MARC21") {
1946         $mintag = "440";
1947         $maxtag = "490";
1948     } else {           # assume unimarc if not marc21
1949         $mintag = "600";
1950         $maxtag = "619";
1951     }
1952
1953     my @marcseries;
1954     my $subjct = "";
1955     my $subfield = "";
1956     my $marcsubjct;
1957
1958     foreach my $field ($record->field('440'), $record->field('490')) {
1959         my @subfields_loop;
1960         #my $value = $field->subfield('a');
1961         #$marcsubjct = {MARCSUBJCT => $value,};
1962         my @subfields = $field->subfields();
1963         #warn "subfields:".join " ", @$subfields;
1964         my $counter = 0;
1965         my @link_loop;
1966         for my $series_subfield (@subfields) {
1967                         my $volume_number;
1968                         undef $volume_number;
1969                         # see if this is an instance of a volume
1970                         if ($series_subfield->[0] eq 'v') {
1971                                 $volume_number=1;
1972                         }
1973
1974             my $code = $series_subfield->[0];
1975             my $value = $series_subfield->[1];
1976             my $linkvalue = $value;
1977             $linkvalue =~ s/(\(|\))//g;
1978             my $operator = " and " unless $counter==0;
1979             push @link_loop, {link => $linkvalue, operator => $operator };
1980             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1981                         if ($volume_number) {
1982                         push @subfields_loop, {volumenum => $value};
1983                         }
1984                         else {
1985             push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number};
1986                         }
1987             $counter++;
1988         }
1989         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1990         #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1991         #push @marcsubjcts, $marcsubjct;
1992         #$subjct = $value;
1993
1994     }
1995     my $marcseriessarray=\@marcseries;
1996     return $marcseriessarray;
1997 }  #end getMARCseriess
1998
1999 =head2 GetFrameworkCode
2000
2001 =over 4
2002
2003     $frameworkcode = GetFrameworkCode( $biblionumber )
2004
2005 =back
2006
2007 =cut
2008
2009 sub GetFrameworkCode {
2010     my ( $biblionumber ) = @_;
2011     my $dbh = C4::Context->dbh;
2012     my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
2013     $sth->execute($biblionumber);
2014     my ($frameworkcode) = $sth->fetchrow;
2015     return $frameworkcode;
2016 }
2017
2018 =head2 GetPublisherNameFromIsbn
2019
2020     $name = GetPublishercodeFromIsbn($isbn);
2021     if(defined $name){
2022         ...
2023     }
2024
2025 =cut
2026
2027 sub GetPublisherNameFromIsbn($){
2028     my $isbn = shift;
2029     $isbn =~ s/[- _]//g;
2030     $isbn =~ s/^0*//;
2031     my @codes = (split '-', DisplayISBN($isbn));
2032     my $code = $codes[0].$codes[1].$codes[2];
2033     my $dbh  = C4::Context->dbh;
2034     my $query = qq{
2035         SELECT distinct publishercode
2036         FROM   biblioitems
2037         WHERE  isbn LIKE ?
2038         AND    publishercode IS NOT NULL
2039         LIMIT 1
2040     };
2041     my $sth = $dbh->prepare($query);
2042     $sth->execute("$code%");
2043     my $name = $sth->fetchrow;
2044     return $name if length $name;
2045     return undef;
2046 }
2047
2048 =head2 TransformKohaToMarc
2049
2050 =over 4
2051
2052     $record = TransformKohaToMarc( $hash )
2053     This function builds partial MARC::Record from a hash
2054     Hash entries can be from biblio or biblioitems.
2055     This function is called in acquisition module, to create a basic catalogue entry from user entry
2056
2057 =back
2058
2059 =cut
2060
2061 sub TransformKohaToMarc {
2062
2063     my ( $hash ) = @_;
2064     my $dbh = C4::Context->dbh;
2065     my $sth =
2066     $dbh->prepare(
2067         "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
2068     );
2069     my $record = MARC::Record->new();
2070     foreach (keys %{$hash}) {
2071         &TransformKohaToMarcOneField( $sth, $record, $_,
2072             $hash->{$_}, '' );
2073         }
2074     return $record;
2075 }
2076
2077 =head2 TransformKohaToMarcOneField
2078
2079 =over 4
2080
2081     $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
2082
2083 =back
2084
2085 =cut
2086
2087 sub TransformKohaToMarcOneField {
2088     my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
2089     $frameworkcode='' unless $frameworkcode;
2090     my $tagfield;
2091     my $tagsubfield;
2092
2093     if ( !defined $sth ) {
2094         my $dbh = C4::Context->dbh;
2095         $sth = $dbh->prepare(
2096             "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
2097         );
2098     }
2099     $sth->execute( $frameworkcode, $kohafieldname );
2100     if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
2101         my $tag = $record->field($tagfield);
2102         if ($tag) {
2103             $tag->update( $tagsubfield => $value );
2104             $record->delete_field($tag);
2105             $record->insert_fields_ordered($tag);
2106         }
2107         else {
2108             $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
2109         }
2110     }
2111     return $record;
2112 }
2113
2114 =head2 TransformHtmlToXml
2115
2116 =over 4
2117
2118 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type )
2119
2120 $auth_type contains :
2121 - nothing : rebuild a biblio, un UNIMARC the encoding is in 100$a pos 26/27
2122 - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
2123 - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
2124
2125 =back
2126
2127 =cut
2128
2129 sub TransformHtmlToXml {
2130     my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
2131     my $xml = MARC::File::XML::header('UTF-8');
2132     $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
2133     MARC::File::XML->default_record_format($auth_type);
2134     # in UNIMARC, field 100 contains the encoding
2135     # check that there is one, otherwise the 
2136     # MARC::Record->new_from_xml will fail (and Koha will die)
2137     my $unimarc_and_100_exist=0;
2138     $unimarc_and_100_exist=1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
2139     my $prevvalue;
2140     my $prevtag = -1;
2141     my $first   = 1;
2142     my $j       = -1;
2143     for ( my $i = 0 ; $i <= @$tags ; $i++ ) {
2144         if (C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a") {
2145             # if we have a 100 field and it's values are not correct, skip them.
2146             # if we don't have any valid 100 field, we will create a default one at the end
2147             my $enc = substr( @$values[$i], 26, 2 );
2148             if ($enc eq '01' or $enc eq '50' or $enc eq '03') {
2149                 $unimarc_and_100_exist=1;
2150             } else {
2151                 next;
2152             }
2153         }
2154         @$values[$i] =~ s/&/&amp;/g;
2155         @$values[$i] =~ s/</&lt;/g;
2156         @$values[$i] =~ s/>/&gt;/g;
2157         @$values[$i] =~ s/"/&quot;/g;
2158         @$values[$i] =~ s/'/&apos;/g;
2159 #         if ( !utf8::is_utf8( @$values[$i] ) ) {
2160 #             utf8::decode( @$values[$i] );
2161 #         }
2162         if ( ( @$tags[$i] ne $prevtag ) ) {
2163             $j++ unless ( @$tags[$i] eq "" );
2164             if ( !$first ) {
2165                 $xml .= "</datafield>\n";
2166                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
2167                     && ( @$values[$i] ne "" ) )
2168                 {
2169                     my $ind1 = substr( @$indicator[$j], 0, 1 );
2170                     my $ind2;
2171                     if ( @$indicator[$j] ) {
2172                         $ind2 = substr( @$indicator[$j], 1, 1 );
2173                     }
2174                     else {
2175                         warn "Indicator in @$tags[$i] is empty";
2176                         $ind2 = " ";
2177                     }
2178                     $xml .=
2179 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2180                     $xml .=
2181 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2182                     $first = 0;
2183                 }
2184                 else {
2185                     $first = 1;
2186                 }
2187             }
2188             else {
2189                 if ( @$values[$i] ne "" ) {
2190
2191                     # leader
2192                     if ( @$tags[$i] eq "000" ) {
2193                         $xml .= "<leader>@$values[$i]</leader>\n";
2194                         $first = 1;
2195
2196                         # rest of the fixed fields
2197                     }
2198                     elsif ( @$tags[$i] < 10 ) {
2199                         $xml .=
2200 "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2201                         $first = 1;
2202                     }
2203                     else {
2204                         my $ind1 = substr( @$indicator[$j], 0, 1 );
2205                         my $ind2 = substr( @$indicator[$j], 1, 1 );
2206                         $xml .=
2207 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2208                         $xml .=
2209 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2210                         $first = 0;
2211                     }
2212                 }
2213             }
2214         }
2215         else {    # @$tags[$i] eq $prevtag
2216             if ( @$values[$i] eq "" ) {
2217             }
2218             else {
2219                 if ($first) {
2220                     my $ind1 = substr( @$indicator[$j], 0, 1 );
2221                     my $ind2 = substr( @$indicator[$j], 1, 1 );
2222                     $xml .=
2223 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2224                     $first = 0;
2225                 }
2226                 $xml .=
2227 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2228             }
2229         }
2230         $prevtag = @$tags[$i];
2231     }
2232     if (C4::Context->preference('marcflavour') and !$unimarc_and_100_exist) {
2233 #     warn "SETTING 100 for $auth_type";
2234         use POSIX qw(strftime);
2235         my $string = strftime( "%Y%m%d", localtime(time) );
2236         # set 50 to position 26 is biblios, 13 if authorities
2237         my $pos=26;
2238         $pos=13 if $auth_type eq 'UNIMARCAUTH';
2239         $string = sprintf( "%-*s", 35, $string );
2240         substr( $string, $pos , 6, "50" );
2241         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2242         $xml .= "<subfield code=\"a\">$string</subfield>\n";
2243         $xml .= "</datafield>\n";
2244     }
2245     $xml .= MARC::File::XML::footer();
2246     return $xml;
2247 }
2248
2249 =head2 TransformHtmlToMarc
2250
2251     L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
2252     L<$params> is a ref to an array as below:
2253     {
2254         'tag_010_indicator_531951' ,
2255         'tag_010_code_a_531951_145735' ,
2256         'tag_010_subfield_a_531951_145735' ,
2257         'tag_200_indicator_873510' ,
2258         'tag_200_code_a_873510_673465' ,
2259         'tag_200_subfield_a_873510_673465' ,
2260         'tag_200_code_b_873510_704318' ,
2261         'tag_200_subfield_b_873510_704318' ,
2262         'tag_200_code_e_873510_280822' ,
2263         'tag_200_subfield_e_873510_280822' ,
2264         'tag_200_code_f_873510_110730' ,
2265         'tag_200_subfield_f_873510_110730' ,
2266     }
2267     L<$cgi> is the CGI object which containts the value.
2268     L<$record> is the MARC::Record object.
2269
2270 =cut
2271
2272 sub TransformHtmlToMarc {
2273     my $params = shift;
2274     my $cgi    = shift;
2275     
2276     # creating a new record
2277     my $record  = MARC::Record->new();
2278     my $i=0;
2279     my @fields;
2280     while ($params->[$i]){ # browse all CGI params
2281         my $param = $params->[$i];
2282         my $newfield=0;
2283         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
2284         if ($param eq 'biblionumber') {
2285             my ( $biblionumbertagfield, $biblionumbertagsubfield ) =
2286                 &GetMarcFromKohaField( "biblio.biblionumber", '' );
2287             if ($biblionumbertagfield < 10) {
2288                 $newfield = MARC::Field->new(
2289                     $biblionumbertagfield,
2290                     $cgi->param($param),
2291                 );
2292             } else {
2293                 $newfield = MARC::Field->new(
2294                     $biblionumbertagfield,
2295                     '',
2296                     '',
2297                     "$biblionumbertagsubfield" => $cgi->param($param),
2298                 );
2299             }
2300             push @fields,$newfield if($newfield);
2301         } 
2302         elsif ($param =~ /^tag_(\d*)_indicator_/){ # new field start when having 'input name="..._indicator_..."
2303             my $tag  = $1;
2304             
2305             my $ind1 = substr($cgi->param($param),0,1);
2306             my $ind2 = substr($cgi->param($param),1,1);
2307             $newfield=0;
2308             my $j=$i+1;
2309             
2310             if($tag < 10){ # no code for theses fields
2311     # in MARC editor, 000 contains the leader.
2312                 if ($tag eq '000' ) {
2313                     $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==25;
2314     # between 001 and 009 (included)
2315                 } else {
2316                     $newfield = MARC::Field->new(
2317                         $tag,
2318                         $cgi->param($params->[$j+1]),
2319                     );
2320                 }
2321     # > 009, deal with subfields
2322             } else {
2323                 while($params->[$j] =~ /_code_/){ # browse all it's subfield
2324                     my $inner_param = $params->[$j];
2325                     if ($newfield){
2326                         if($cgi->param($params->[$j+1])){  # only if there is a value (code => value)
2327                             $newfield->add_subfields(
2328                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
2329                             );
2330                         }
2331                     } else {
2332                         if ( $cgi->param($params->[$j+1]) ) { # creating only if there is a value (code => value)
2333                             $newfield = MARC::Field->new(
2334                                 $tag,
2335                                 ''.$ind1,
2336                                 ''.$ind2,
2337                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1]),
2338                             );
2339                         }
2340                     }
2341                     $j+=2;
2342                 }
2343             }
2344             push @fields,$newfield if($newfield);
2345         }
2346         $i++;
2347     }
2348     
2349     $record->append_fields(@fields);
2350     warn "RESULT : ".$record->as_formatted;
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