Changing to two-column layout
[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]))==24;
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     return $record;
2351 }
2352
2353 =head2 TransformMarcToKoha
2354
2355 =over 4
2356
2357         $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2358
2359 =back
2360
2361 =cut
2362
2363 sub TransformMarcToKoha {
2364     my ( $dbh, $record, $frameworkcode, $table ) = @_;
2365
2366     my $result;
2367
2368     # sometimes we only want to return the items data
2369     if ($table eq 'items') {
2370         my $sth = $dbh->prepare("SHOW COLUMNS FROM items");
2371         $sth->execute();
2372         while ( (my $field) = $sth->fetchrow ) {
2373             my $value = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2374             my $key = _disambiguate($table, $field);
2375             if ($result->{$key}) {
2376                 $result->{$key} .= " | " . $value;
2377             } else {
2378                 $result->{$key} = $value;
2379             }
2380         }
2381         return $result;
2382     } else {
2383         my @tables = ('biblio','biblioitems','items');
2384         foreach my $table (@tables){
2385             my $sth2 = $dbh->prepare("SHOW COLUMNS from $table");
2386             $sth2->execute;
2387             while (my ($field) = $sth2->fetchrow){
2388                 # FIXME use of _disambiguate is a temporary hack
2389                 # $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2390                 my $value = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2391                 my $key = _disambiguate($table, $field);
2392                 if ($result->{$key}) {
2393                     # FIXME - hack to not bring in duplicates of the same value
2394                     unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
2395                         $result->{$key} .= " | " . $value;
2396                     }
2397                 } else {
2398                     $result->{$key} = $value;
2399                 }
2400             }
2401             $sth2->finish();
2402         }
2403         # modify copyrightdate to keep only the 1st year found
2404         my $temp = $result->{'copyrightdate'};
2405         $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
2406         if ( $1 > 0 ) {
2407             $result->{'copyrightdate'} = $1;
2408         }
2409         else {                      # if no cYYYY, get the 1st date.
2410             $temp =~ m/(\d\d\d\d)/;
2411             $result->{'copyrightdate'} = $1;
2412         }
2413     
2414         # modify publicationyear to keep only the 1st year found
2415         $temp = $result->{'publicationyear'};
2416         $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
2417         if ( $1 > 0 ) {
2418             $result->{'publicationyear'} = $1;
2419         }
2420         else {                      # if no cYYYY, get the 1st date.
2421             $temp =~ m/(\d\d\d\d)/;
2422             $result->{'publicationyear'} = $1;
2423         }
2424         return $result;
2425     }
2426 }
2427
2428
2429 =head2 _disambiguate
2430
2431 =over 4
2432
2433 $newkey = _disambiguate($table, $field);
2434
2435 This is a temporary hack to distinguish between the
2436 following sets of columns when using TransformMarcToKoha.
2437
2438 items.cn_source & biblioitems.cn_source
2439 items.cn_sort & biblioitems.cn_sort
2440
2441 Columns that are currently NOT distinguished (FIXME
2442 due to lack of time to fully test) are:
2443
2444 biblio.notes and biblioitems.notes
2445 biblionumber
2446 timestamp
2447 biblioitemnumber
2448
2449 FIXME - this is necessary because prefixing each column
2450 name with the table name would require changing lots
2451 of code and templates, and exposing more of the DB
2452 structure than is good to the UI templates, particularly
2453 since biblio and bibloitems may well merge in a future
2454 version.  In the future, it would also be good to 
2455 separate DB access and UI presentation field names
2456 more.
2457
2458 =back
2459
2460 =cut
2461
2462 sub _disambiguate {
2463     my ($table, $column) = @_;
2464     if ($column eq "cn_sort" or $column eq "cn_source") {
2465         return $table . '.' . $column;
2466     } else {
2467         return $column;
2468     }
2469
2470 }
2471
2472 =head2 get_koha_field_from_marc
2473
2474 =over 4
2475
2476 $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2477
2478 Internal function to map data from the MARC record to a specific non-MARC field.
2479 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2480
2481 =back
2482
2483 =cut
2484
2485 sub get_koha_field_from_marc {
2486     my ($koha_table,$koha_column,$record,$frameworkcode) = @_;
2487     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table.'.'.$koha_column, $frameworkcode );  
2488     my $kohafield;
2489     foreach my $field ( $record->field($tagfield) ) {
2490         if ( $field->tag() < 10 ) {
2491             if ( $kohafield ) {
2492                 $kohafield .= " | " . $field->data();
2493             }
2494             else {
2495                 $kohafield = $field->data();
2496             }
2497         }
2498         else {
2499             if ( $field->subfields ) {
2500                 my @subfields = $field->subfields();
2501                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2502                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2503                         if ( $kohafield ) {
2504                             $kohafield .=
2505                               " | " . $subfields[$subfieldcount][1];
2506                         }
2507                         else {
2508                             $kohafield =
2509                               $subfields[$subfieldcount][1];
2510                         }
2511                     }
2512                 }
2513             }
2514         }
2515     }
2516     return $kohafield;
2517
2518
2519
2520 =head2 TransformMarcToKohaOneField
2521
2522 =over 4
2523
2524 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2525
2526 =back
2527
2528 =cut
2529
2530 sub TransformMarcToKohaOneField {
2531
2532     # FIXME ? if a field has a repeatable subfield that is used in old-db,
2533     # only the 1st will be retrieved...
2534     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2535     my $res = "";
2536     my ( $tagfield, $subfield ) =
2537       GetMarcFromKohaField( $kohatable . "." . $kohafield,
2538         $frameworkcode );
2539     foreach my $field ( $record->field($tagfield) ) {
2540         if ( $field->tag() < 10 ) {
2541             if ( $result->{$kohafield} ) {
2542                 $result->{$kohafield} .= " | " . $field->data();
2543             }
2544             else {
2545                 $result->{$kohafield} = $field->data();
2546             }
2547         }
2548         else {
2549             if ( $field->subfields ) {
2550                 my @subfields = $field->subfields();
2551                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2552                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2553                         if ( $result->{$kohafield} ) {
2554                             $result->{$kohafield} .=
2555                               " | " . $subfields[$subfieldcount][1];
2556                         }
2557                         else {
2558                             $result->{$kohafield} =
2559                               $subfields[$subfieldcount][1];
2560                         }
2561                     }
2562                 }
2563             }
2564         }
2565     }
2566     return $result;
2567 }
2568
2569 =head1  OTHER FUNCTIONS
2570
2571 =head2 char_decode
2572
2573 =over 4
2574
2575 my $string = char_decode( $string, $encoding );
2576
2577 converts ISO 5426 coded string to UTF-8
2578 sloppy code : should be improved in next issue
2579
2580 =back
2581
2582 =cut
2583
2584 sub char_decode {
2585     my ( $string, $encoding ) = @_;
2586     $_ = $string;
2587
2588     $encoding = C4::Context->preference("marcflavour") unless $encoding;
2589     if ( $encoding eq "UNIMARC" ) {
2590
2591         #         s/\xe1/Æ/gm;
2592         s/\xe2/Ğ/gm;
2593         s/\xe9/Ø/gm;
2594         s/\xec/ş/gm;
2595         s/\xf1/æ/gm;
2596         s/\xf3/ğ/gm;
2597         s/\xf9/ø/gm;
2598         s/\xfb/ß/gm;
2599         s/\xc1\x61/à/gm;
2600         s/\xc1\x65/è/gm;
2601         s/\xc1\x69/ì/gm;
2602         s/\xc1\x6f/ò/gm;
2603         s/\xc1\x75/ù/gm;
2604         s/\xc1\x41/À/gm;
2605         s/\xc1\x45/È/gm;
2606         s/\xc1\x49/Ì/gm;
2607         s/\xc1\x4f/Ò/gm;
2608         s/\xc1\x55/Ù/gm;
2609         s/\xc2\x41/Á/gm;
2610         s/\xc2\x45/É/gm;
2611         s/\xc2\x49/Í/gm;
2612         s/\xc2\x4f/Ó/gm;
2613         s/\xc2\x55/Ú/gm;
2614         s/\xc2\x59/İ/gm;
2615         s/\xc2\x61/á/gm;
2616         s/\xc2\x65/é/gm;
2617         s/\xc2\x69/í/gm;
2618         s/\xc2\x6f/ó/gm;
2619         s/\xc2\x75/ú/gm;
2620         s/\xc2\x79/ı/gm;
2621         s/\xc3\x41/Â/gm;
2622         s/\xc3\x45/Ê/gm;
2623         s/\xc3\x49/Î/gm;
2624         s/\xc3\x4f/Ô/gm;
2625         s/\xc3\x55/Û/gm;
2626         s/\xc3\x61/â/gm;
2627         s/\xc3\x65/ê/gm;
2628         s/\xc3\x69/î/gm;
2629         s/\xc3\x6f/ô/gm;
2630         s/\xc3\x75/û/gm;
2631         s/\xc4\x41/Ã/gm;
2632         s/\xc4\x4e/Ñ/gm;
2633         s/\xc4\x4f/Õ/gm;
2634         s/\xc4\x61/ã/gm;
2635         s/\xc4\x6e/ñ/gm;
2636         s/\xc4\x6f/õ/gm;
2637         s/\xc8\x41/Ä/gm;
2638         s/\xc8\x45/Ë/gm;
2639         s/\xc8\x49/Ï/gm;
2640         s/\xc8\x61/ä/gm;
2641         s/\xc8\x65/ë/gm;
2642         s/\xc8\x69/ï/gm;
2643         s/\xc8\x6F/ö/gm;
2644         s/\xc8\x75/ü/gm;
2645         s/\xc8\x76/ÿ/gm;
2646         s/\xc9\x41/Ä/gm;
2647         s/\xc9\x45/Ë/gm;
2648         s/\xc9\x49/Ï/gm;
2649         s/\xc9\x4f/Ö/gm;
2650         s/\xc9\x55/Ü/gm;
2651         s/\xc9\x61/ä/gm;
2652         s/\xc9\x6f/ö/gm;
2653         s/\xc9\x75/ü/gm;
2654         s/\xca\x41/Å/gm;
2655         s/\xca\x61/å/gm;
2656         s/\xd0\x43/Ç/gm;
2657         s/\xd0\x63/ç/gm;
2658
2659         # this handles non-sorting blocks (if implementation requires this)
2660         $string = nsb_clean($_);
2661     }
2662     elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
2663         ##MARC-8 to UTF-8
2664
2665         s/\xe1\x61/à/gm;
2666         s/\xe1\x65/è/gm;
2667         s/\xe1\x69/ì/gm;
2668         s/\xe1\x6f/ò/gm;
2669         s/\xe1\x75/ù/gm;
2670         s/\xe1\x41/À/gm;
2671         s/\xe1\x45/È/gm;
2672         s/\xe1\x49/Ì/gm;
2673         s/\xe1\x4f/Ò/gm;
2674         s/\xe1\x55/Ù/gm;
2675         s/\xe2\x41/Á/gm;
2676         s/\xe2\x45/É/gm;
2677         s/\xe2\x49/Í/gm;
2678         s/\xe2\x4f/Ó/gm;
2679         s/\xe2\x55/Ú/gm;
2680         s/\xe2\x59/İ/gm;
2681         s/\xe2\x61/á/gm;
2682         s/\xe2\x65/é/gm;
2683         s/\xe2\x69/í/gm;
2684         s/\xe2\x6f/ó/gm;
2685         s/\xe2\x75/ú/gm;
2686         s/\xe2\x79/ı/gm;
2687         s/\xe3\x41/Â/gm;
2688         s/\xe3\x45/Ê/gm;
2689         s/\xe3\x49/Î/gm;
2690         s/\xe3\x4f/Ô/gm;
2691         s/\xe3\x55/Û/gm;
2692         s/\xe3\x61/â/gm;
2693         s/\xe3\x65/ê/gm;
2694         s/\xe3\x69/î/gm;
2695         s/\xe3\x6f/ô/gm;
2696         s/\xe3\x75/û/gm;
2697         s/\xe4\x41/Ã/gm;
2698         s/\xe4\x4e/Ñ/gm;
2699         s/\xe4\x4f/Õ/gm;
2700         s/\xe4\x61/ã/gm;
2701         s/\xe4\x6e/ñ/gm;
2702         s/\xe4\x6f/õ/gm;
2703         s/\xe6\x41/Ă/gm;
2704         s/\xe6\x45/Ĕ/gm;
2705         s/\xe6\x65/ĕ/gm;
2706         s/\xe6\x61/ă/gm;
2707         s/\xe8\x45/Ë/gm;
2708         s/\xe8\x49/Ï/gm;
2709         s/\xe8\x65/ë/gm;
2710         s/\xe8\x69/ï/gm;
2711         s/\xe8\x76/ÿ/gm;
2712         s/\xe9\x41/A/gm;
2713         s/\xe9\x4f/O/gm;
2714         s/\xe9\x55/U/gm;
2715         s/\xe9\x61/a/gm;
2716         s/\xe9\x6f/o/gm;
2717         s/\xe9\x75/u/gm;
2718         s/\xea\x41/A/gm;
2719         s/\xea\x61/a/gm;
2720
2721         #Additional Turkish characters
2722         s/\x1b//gm;
2723         s/\x1e//gm;
2724         s/(\xf0)s/\xc5\x9f/gm;
2725         s/(\xf0)S/\xc5\x9e/gm;
2726         s/(\xf0)c/ç/gm;
2727         s/(\xf0)C/Ç/gm;
2728         s/\xe7\x49/\\xc4\xb0/gm;
2729         s/(\xe6)G/\xc4\x9e/gm;
2730         s/(\xe6)g/ğ\xc4\x9f/gm;
2731         s/\xB8/ı/gm;
2732         s/\xB9/£/gm;
2733         s/(\xe8|\xc8)o/ö/gm;
2734         s/(\xe8|\xc8)O/Ö/gm;
2735         s/(\xe8|\xc8)u/ü/gm;
2736         s/(\xe8|\xc8)U/Ü/gm;
2737         s/\xc2\xb8/\xc4\xb1/gm;
2738         s/¸/\xc4\xb1/gm;
2739
2740         # this handles non-sorting blocks (if implementation requires this)
2741         $string = nsb_clean($_);
2742     }
2743     return ($string);
2744 }
2745
2746 =head2 nsb_clean
2747
2748 =over 4
2749
2750 my $string = nsb_clean( $string, $encoding );
2751
2752 =back
2753
2754 =cut
2755
2756 sub nsb_clean {
2757     my $NSB      = '\x88';    # NSB : begin Non Sorting Block
2758     my $NSE      = '\x89';    # NSE : Non Sorting Block end
2759                               # handles non sorting blocks
2760     my ($string) = @_;
2761     $_ = $string;
2762     s/$NSB/(/gm;
2763     s/[ ]{0,1}$NSE/) /gm;
2764     $string = $_;
2765     return ($string);
2766 }
2767
2768 =head2 PrepareItemrecordDisplay
2769
2770 =over 4
2771
2772 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber);
2773
2774 Returns a hash with all the fields for Display a given item data in a template
2775
2776 =back
2777
2778 =cut
2779
2780 sub PrepareItemrecordDisplay {
2781
2782     my ( $bibnum, $itemnum ) = @_;
2783
2784     my $dbh = C4::Context->dbh;
2785     my $frameworkcode = &GetFrameworkCode( $bibnum );
2786     my ( $itemtagfield, $itemtagsubfield ) =
2787       &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2788     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2789     my $itemrecord = GetMarcItem( $bibnum, $itemnum) if ($itemnum);
2790     my @loop_data;
2791     my $authorised_values_sth =
2792       $dbh->prepare(
2793 "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"
2794       );
2795     foreach my $tag ( sort keys %{$tagslib} ) {
2796         my $previous_tag = '';
2797         if ( $tag ne '' ) {
2798             # loop through each subfield
2799             my $cntsubf;
2800             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2801                 next if ( subfield_is_koha_internal_p($subfield) );
2802                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2803                 my %subfield_data;
2804                 $subfield_data{tag}           = $tag;
2805                 $subfield_data{subfield}      = $subfield;
2806                 $subfield_data{countsubfield} = $cntsubf++;
2807                 $subfield_data{kohafield}     =
2808                   $tagslib->{$tag}->{$subfield}->{'kohafield'};
2809
2810          #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2811                 $subfield_data{marc_lib} =
2812                     "<span id=\"error\" title=\""
2813                   . $tagslib->{$tag}->{$subfield}->{lib} . "\">"
2814                   . substr( $tagslib->{$tag}->{$subfield}->{lib}, 0, 12 )
2815                   . "</span>";
2816                 $subfield_data{mandatory} =
2817                   $tagslib->{$tag}->{$subfield}->{mandatory};
2818                 $subfield_data{repeatable} =
2819                   $tagslib->{$tag}->{$subfield}->{repeatable};
2820                 $subfield_data{hidden} = "display:none"
2821                   if $tagslib->{$tag}->{$subfield}->{hidden};
2822                 my ( $x, $value );
2823                 ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord )
2824                   if ($itemrecord);
2825                 $value =~ s/"/&quot;/g;
2826
2827                 # search for itemcallnumber if applicable
2828                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2829                     'items.itemcallnumber'
2830                     && C4::Context->preference('itemcallnumber') )
2831                 {
2832                     my $CNtag =
2833                       substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2834                     my $CNsubfield =
2835                       substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2836                     my $temp = $itemrecord->field($CNtag) if ($itemrecord);
2837                     if ($temp) {
2838                         $value = $temp->subfield($CNsubfield);
2839                     }
2840                 }
2841                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2842                     my @authorised_values;
2843                     my %authorised_lib;
2844
2845                     # builds list, depending on authorised value...
2846                     #---- branch
2847                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
2848                         "branches" )
2849                     {
2850                         if ( ( C4::Context->preference("IndependantBranches") )
2851                             && ( C4::Context->userenv->{flags} != 1 ) )
2852                         {
2853                             my $sth =
2854                               $dbh->prepare(
2855                                                                 "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname"
2856                               );
2857                             $sth->execute( C4::Context->userenv->{branch} );
2858                             push @authorised_values, ""
2859                               unless (
2860                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2861                             while ( my ( $branchcode, $branchname ) =
2862                                 $sth->fetchrow_array )
2863                             {
2864                                 push @authorised_values, $branchcode;
2865                                 $authorised_lib{$branchcode} = $branchname;
2866                             }
2867                         }
2868                         else {
2869                             my $sth =
2870                               $dbh->prepare(
2871                                                                 "SELECT branchcode,branchname FROM branches ORDER BY branchname"
2872                               );
2873                             $sth->execute;
2874                             push @authorised_values, ""
2875                               unless (
2876                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2877                             while ( my ( $branchcode, $branchname ) =
2878                                 $sth->fetchrow_array )
2879                             {
2880                                 push @authorised_values, $branchcode;
2881                                 $authorised_lib{$branchcode} = $branchname;
2882                             }
2883                         }
2884
2885                         #----- itemtypes
2886                     }
2887                     elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
2888                         "itemtypes" )
2889                     {
2890                         my $sth =
2891                           $dbh->prepare(
2892                                                         "SELECT itemtype,description FROM itemtypes ORDER BY description"
2893                           );
2894                         $sth->execute;
2895                         push @authorised_values, ""
2896                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2897                         while ( my ( $itemtype, $description ) =
2898                             $sth->fetchrow_array )
2899                         {
2900                             push @authorised_values, $itemtype;
2901                             $authorised_lib{$itemtype} = $description;
2902                         }
2903
2904                         #---- "true" authorised value
2905                     }
2906                     else {
2907                         $authorised_values_sth->execute(
2908                             $tagslib->{$tag}->{$subfield}->{authorised_value} );
2909                         push @authorised_values, ""
2910                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2911                         while ( my ( $value, $lib ) =
2912                             $authorised_values_sth->fetchrow_array )
2913                         {
2914                             push @authorised_values, $value;
2915                             $authorised_lib{$value} = $lib;
2916                         }
2917                     }
2918                     $subfield_data{marc_value} = CGI::scrolling_list(
2919                         -name     => 'field_value',
2920                         -values   => \@authorised_values,
2921                         -default  => "$value",
2922                         -labels   => \%authorised_lib,
2923                         -size     => 1,
2924                         -tabindex => '',
2925                         -multiple => 0,
2926                     );
2927                 }
2928                 elsif ( $tagslib->{$tag}->{$subfield}->{thesaurus_category} ) {
2929                     $subfield_data{marc_value} =
2930 "<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>";
2931
2932 #"
2933 # COMMENTED OUT because No $i is provided with this API.
2934 # And thus, no value_builder can be activated.
2935 # BUT could be thought over.
2936 #         } elsif ($tagslib->{$tag}->{$subfield}->{'value_builder'}) {
2937 #             my $plugin="value_builder/".$tagslib->{$tag}->{$subfield}->{'value_builder'};
2938 #             require $plugin;
2939 #             my $extended_param = plugin_parameters($dbh,$itemrecord,$tagslib,$i,0);
2940 #             my ($function_name,$javascript) = plugin_javascript($dbh,$record,$tagslib,$i,0);
2941 #             $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";
2942                 }
2943                 else {
2944                     $subfield_data{marc_value} =
2945 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=50 maxlength=255>";
2946                 }
2947                 push( @loop_data, \%subfield_data );
2948             }
2949         }
2950     }
2951     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2952       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2953     return {
2954         'itemtagfield'    => $itemtagfield,
2955         'itemtagsubfield' => $itemtagsubfield,
2956         'itemnumber'      => $itemnumber,
2957         'iteminformation' => \@loop_data
2958     };
2959 }
2960 #"
2961
2962 #
2963 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2964 # at the same time
2965 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2966 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2967 # =head2 ModZebrafiles
2968
2969 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2970
2971 # =cut
2972
2973 # sub ModZebrafiles {
2974
2975 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2976
2977 #     my $op;
2978 #     my $zebradir =
2979 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2980 #     unless ( opendir( DIR, "$zebradir" ) ) {
2981 #         warn "$zebradir not found";
2982 #         return;
2983 #     }
2984 #     closedir DIR;
2985 #     my $filename = $zebradir . $biblionumber;
2986
2987 #     if ($record) {
2988 #         open( OUTPUT, ">", $filename . ".xml" );
2989 #         print OUTPUT $record;
2990 #         close OUTPUT;
2991 #     }
2992 # }
2993
2994 =head2 ModZebra
2995
2996 =over 4
2997
2998 ModZebra( $biblionumber, $op, $server, $newRecord );
2999
3000     $biblionumber is the biblionumber we want to index
3001     $op is specialUpdate or delete, and is used to know what we want to do
3002     $server is the server that we want to update
3003     $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.
3004     
3005 =back
3006
3007 =cut
3008
3009 sub ModZebra {
3010 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
3011     my ( $biblionumber, $op, $server, $newRecord ) = @_;
3012     my $dbh=C4::Context->dbh;
3013
3014     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
3015     # at the same time
3016     # replaced by a zebraqueue table, that is filled with ModZebra to run.
3017     # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
3018
3019     if (C4::Context->preference("NoZebra")) {
3020         # lock the nozebra table : we will read index lines, update them in Perl process
3021         # and write everything in 1 transaction.
3022         # lock the table to avoid someone else overwriting what we are doing
3023         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE');
3024         my %result; # the result hash that will be builded by deletion / add, and written on mySQL at the end, to improve speed
3025         my $record;
3026         if ($server eq 'biblioserver') {
3027             $record= GetMarcBiblio($biblionumber);
3028         } else {
3029             $record= C4::AuthoritiesMarc::GetAuthority($biblionumber);
3030         }
3031         if ($op eq 'specialUpdate') {
3032             # OK, we have to add or update the record
3033             # 1st delete (virtually, in indexes) ...
3034             %result = _DelBiblioNoZebra($biblionumber,$record,$server);
3035             # ... add the record
3036             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
3037         } else {
3038             # it's a deletion, delete the record...
3039             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
3040             %result=_DelBiblioNoZebra($biblionumber,$record,$server);
3041         }
3042         # ok, now update the database...
3043         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
3044         foreach my $key (keys %result) {
3045             foreach my $index (keys %{$result{$key}}) {
3046                 $sth->execute($result{$key}->{$index}, $server, $key, $index);
3047             }
3048         }
3049         $dbh->do('UNLOCK TABLES');
3050
3051     } else {
3052         #
3053         # we use zebra, just fill zebraqueue table
3054         #
3055         my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
3056         $sth->execute($biblionumber,$server,$op);
3057         $sth->finish;
3058     }
3059 }
3060
3061 =head2 GetNoZebraIndexes
3062
3063     %indexes = GetNoZebraIndexes;
3064     
3065     return the data from NoZebraIndexes syspref.
3066
3067 =cut
3068
3069 sub GetNoZebraIndexes {
3070     my $index = C4::Context->preference('NoZebraIndexes');
3071     my %indexes;
3072     foreach my $line (split /('|"),/,$index) {
3073         $line =~ /(.*)=>(.*)/;
3074         my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
3075         my $fields = $2;
3076         $index =~ s/'|"| //g;
3077         $fields =~ s/'|"| //g;
3078         $indexes{$index}=$fields;
3079     }
3080     return %indexes;
3081 }
3082
3083 =head1 INTERNAL FUNCTIONS
3084
3085 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
3086
3087     function to delete a biblio in NoZebra indexes
3088     This function does NOT delete anything in database : it reads all the indexes entries
3089     that have to be deleted & delete them in the hash
3090     The SQL part is done either :
3091     - after the Add if we are modifying a biblio (delete + add again)
3092     - immediatly after this sub if we are doing a true deletion.
3093     $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
3094
3095 =cut
3096
3097
3098 sub _DelBiblioNoZebra {
3099     my ($biblionumber, $record, $server)=@_;
3100     
3101     # Get the indexes
3102     my $dbh = C4::Context->dbh;
3103     # Get the indexes
3104     my %index;
3105     my $title;
3106     if ($server eq 'biblioserver') {
3107         %index=GetNoZebraIndexes;
3108         # get title of the record (to store the 10 first letters with the index)
3109         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
3110         $title = lc($record->subfield($titletag,$titlesubfield));
3111     } else {
3112         # for authorities, the "title" is the $a mainentry
3113         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
3114         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
3115         $title = $record->subfield($authref->{auth_tag_to_report},'a');
3116         $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
3117         $index{'mainentry'}    = $authref->{'auth_tag_to_report'}.'*';
3118         $index{'auth_type'}    = '152b';
3119     }
3120     
3121     my %result;
3122     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
3123     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
3124     # limit to 10 char, should be enough, and limit the DB size
3125     $title = substr($title,0,10);
3126     #parse each field
3127     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
3128     foreach my $field ($record->fields()) {
3129         #parse each subfield
3130         next if $field->tag <10;
3131         foreach my $subfield ($field->subfields()) {
3132             my $tag = $field->tag();
3133             my $subfieldcode = $subfield->[0];
3134             my $indexed=0;
3135             # check each index to see if the subfield is stored somewhere
3136             # otherwise, store it in __RAW__ index
3137             foreach my $key (keys %index) {
3138 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
3139                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
3140                     $indexed=1;
3141                     my $line= lc $subfield->[1];
3142                     # remove meaningless value in the field...
3143                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3144                     # ... and split in words
3145                     foreach (split / /,$line) {
3146                         next unless $_; # skip  empty values (multiple spaces)
3147                         # if the entry is already here, do nothing, the biblionumber has already be removed
3148                         unless ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
3149                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
3150                             $sth2->execute($server,$key,$_);
3151                             my $existing_biblionumbers = $sth2->fetchrow;
3152                             # it exists
3153                             if ($existing_biblionumbers) {
3154 #                                 warn " existing for $key $_: $existing_biblionumbers";
3155                                 $result{$key}->{$_} =$existing_biblionumbers;
3156                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3157                             }
3158                         }
3159                     }
3160                 }
3161             }
3162             # the subfield is not indexed, store it in __RAW__ index anyway
3163             unless ($indexed) {
3164                 my $line= lc $subfield->[1];
3165                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3166                 # ... and split in words
3167                 foreach (split / /,$line) {
3168                     next unless $_; # skip  empty values (multiple spaces)
3169                     # if the entry is already here, do nothing, the biblionumber has already be removed
3170                     unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
3171                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
3172                         $sth2->execute($server,'__RAW__',$_);
3173                         my $existing_biblionumbers = $sth2->fetchrow;
3174                         # it exists
3175                         if ($existing_biblionumbers) {
3176                             $result{'__RAW__'}->{$_} =$existing_biblionumbers;
3177                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
3178                         }
3179                     }
3180                 }
3181             }
3182         }
3183     }
3184     return %result;
3185 }
3186
3187 =head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
3188
3189     function to add a biblio in NoZebra indexes
3190
3191 =cut
3192
3193 sub _AddBiblioNoZebra {
3194     my ($biblionumber, $record, $server, %result)=@_;
3195     my $dbh = C4::Context->dbh;
3196     # Get the indexes
3197     my %index;
3198     my $title;
3199     if ($server eq 'biblioserver') {
3200         %index=GetNoZebraIndexes;
3201         # get title of the record (to store the 10 first letters with the index)
3202         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
3203         $title = lc($record->subfield($titletag,$titlesubfield));
3204     } else {
3205         # warn "server : $server";
3206         # for authorities, the "title" is the $a mainentry
3207         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
3208         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
3209         $title = $record->subfield($authref->{auth_tag_to_report},'a');
3210         $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
3211         $index{'mainentry'}     = $authref->{auth_tag_to_report}.'*';
3212         $index{'auth_type'}     = '152b';
3213     }
3214
3215     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
3216     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
3217     # limit to 10 char, should be enough, and limit the DB size
3218     $title = substr($title,0,10);
3219     #parse each field
3220     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
3221     foreach my $field ($record->fields()) {
3222         #parse each subfield
3223         next if $field->tag <10;
3224         foreach my $subfield ($field->subfields()) {
3225             my $tag = $field->tag();
3226             my $subfieldcode = $subfield->[0];
3227             my $indexed=0;
3228             # check each index to see if the subfield is stored somewhere
3229             # otherwise, store it in __RAW__ index
3230             foreach my $key (keys %index) {
3231 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
3232                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
3233                     $indexed=1;
3234                     my $line= lc $subfield->[1];
3235                     # remove meaningless value in the field...
3236                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3237                     # ... and split in words
3238                     foreach (split / /,$line) {
3239                         next unless $_; # skip  empty values (multiple spaces)
3240                         # if the entry is already here, improve weight
3241 #                         warn "managing $_";
3242                         if ($result{$key}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
3243                             my $weight=$1+1;
3244                             $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3245                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
3246                         } else {
3247                             # get the value if it exist in the nozebra table, otherwise, create it
3248                             $sth2->execute($server,$key,$_);
3249                             my $existing_biblionumbers = $sth2->fetchrow;
3250                             # it exists
3251                             if ($existing_biblionumbers) {
3252                                 $result{$key}->{"$_"} =$existing_biblionumbers;
3253                                 my $weight=$1+1;
3254                                 $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3255                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
3256                             # create a new ligne for this entry
3257                             } else {
3258 #                             warn "INSERT : $server / $key / $_";
3259                                 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
3260                                 $result{$key}->{"$_"}.="$biblionumber,$title-1;";
3261                             }
3262                         }
3263                     }
3264                 }
3265             }
3266             # the subfield is not indexed, store it in __RAW__ index anyway
3267             unless ($indexed) {
3268                 my $line= lc $subfield->[1];
3269                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
3270                 # ... and split in words
3271                 foreach (split / /,$line) {
3272                     next unless $_; # skip  empty values (multiple spaces)
3273                     # if the entry is already here, improve weight
3274                     if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
3275                         my $weight=$1+1;
3276                         $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3277                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
3278                     } else {
3279                         # get the value if it exist in the nozebra table, otherwise, create it
3280                         $sth2->execute($server,'__RAW__',$_);
3281                         my $existing_biblionumbers = $sth2->fetchrow;
3282                         # it exists
3283                         if ($existing_biblionumbers) {
3284                             $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
3285                             my $weight=$1+1;
3286                             $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
3287                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
3288                         # create a new ligne for this entry
3289                         } else {
3290                             $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).',  indexname="__RAW__",value='.$dbh->quote($_));
3291                             $result{'__RAW__'}->{"$_"}.="$biblionumber,$title-1;";
3292                         }
3293                     }
3294                 }
3295             }
3296         }
3297     }
3298     return %result;
3299 }
3300
3301
3302 =head2 MARCitemchange
3303
3304 =over 4
3305
3306 &MARCitemchange( $record, $itemfield, $newvalue )
3307
3308 Function to update a single value in an item field.
3309 Used twice, could probably be replaced by something else, but works well...
3310
3311 =back
3312
3313 =back
3314
3315 =cut
3316
3317 sub MARCitemchange {
3318     my ( $record, $itemfield, $newvalue ) = @_;
3319     my $dbh = C4::Context->dbh;
3320     
3321     my ( $tagfield, $tagsubfield ) =
3322       GetMarcFromKohaField( $itemfield, "" );
3323     if ( ($tagfield) && ($tagsubfield) ) {
3324         my $tag = $record->field($tagfield);
3325         if ($tag) {
3326             $tag->update( $tagsubfield => $newvalue );
3327             $record->delete_field($tag);
3328             $record->insert_fields_ordered($tag);
3329         }
3330     }
3331 }
3332 =head2 _find_value
3333
3334 =over 4
3335
3336 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
3337
3338 Find the given $subfield in the given $tag in the given
3339 MARC::Record $record.  If the subfield is found, returns
3340 the (indicators, value) pair; otherwise, (undef, undef) is
3341 returned.
3342
3343 PROPOSITION :
3344 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
3345 I suggest we export it from this module.
3346
3347 =back
3348
3349 =cut
3350
3351 sub _find_value {
3352     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
3353     my @result;
3354     my $indicator;
3355     if ( $tagfield < 10 ) {
3356         if ( $record->field($tagfield) ) {
3357             push @result, $record->field($tagfield)->data();
3358         }
3359         else {
3360             push @result, "";
3361         }
3362     }
3363     else {
3364         foreach my $field ( $record->field($tagfield) ) {
3365             my @subfields = $field->subfields();
3366             foreach my $subfield (@subfields) {
3367                 if ( @$subfield[0] eq $insubfield ) {
3368                     push @result, @$subfield[1];
3369                     $indicator = $field->indicator(1) . $field->indicator(2);
3370                 }
3371             }
3372         }
3373     }
3374     return ( $indicator, @result );
3375 }
3376
3377 =head2 _koha_marc_update_bib_ids
3378
3379 =over 4
3380
3381 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
3382
3383 Internal function to add or update biblionumber and biblioitemnumber to
3384 the MARC XML.
3385
3386 =back
3387
3388 =cut
3389
3390 sub _koha_marc_update_bib_ids {
3391     my ($record, $frameworkcode, $biblionumber, $biblioitemnumber) = @_;
3392
3393     # we must add bibnum and bibitemnum in MARC::Record...
3394     # we build the new field with biblionumber and biblioitemnumber
3395     # we drop the original field
3396     # we add the new builded field.
3397     my ($biblio_tag, $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
3398     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
3399
3400     if ($biblio_tag != $biblioitem_tag) {
3401         # biblionumber & biblioitemnumber are in different fields
3402
3403         # deal with biblionumber
3404         my ($new_field, $old_field);
3405         if ($biblio_tag < 10) {
3406             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
3407         } else {
3408             $new_field =
3409               MARC::Field->new( $biblio_tag, '', '',
3410                 "$biblio_subfield" => $biblionumber );
3411         }
3412
3413         # drop old field and create new one...
3414         $old_field = $record->field($biblio_tag);
3415         $record->delete_field($old_field);
3416         $record->append_fields($new_field);
3417
3418         # deal with biblioitemnumber
3419         if ($biblioitem_tag < 10) {
3420             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
3421         } else {
3422             $new_field =
3423               MARC::Field->new( $biblioitem_tag, '', '',
3424                 "$biblioitem_subfield" => $biblioitemnumber, );
3425         }
3426         # drop old field and create new one...
3427         $old_field = $record->field($biblioitem_tag);
3428         $record->delete_field($old_field);
3429         $record->insert_fields_ordered($new_field);
3430
3431     } else {
3432         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
3433         my $new_field = MARC::Field->new(
3434             $biblio_tag, '', '',
3435             "$biblio_subfield" => $biblionumber,
3436             "$biblioitem_subfield" => $biblioitemnumber
3437         );
3438
3439         # drop old field and create new one...
3440         my $old_field = $record->field($biblio_tag);
3441         $record->delete_field($old_field);
3442         $record->insert_fields_ordered($new_field);
3443     }
3444 }
3445
3446 =head2 _koha_add_biblio
3447
3448 =over 4
3449
3450 my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
3451
3452 Internal function to add a biblio ($biblio is a hash with the values)
3453
3454 =back
3455
3456 =cut
3457
3458 sub _koha_add_biblio {
3459     my ( $dbh, $biblio, $frameworkcode ) = @_;
3460
3461         my $error;
3462
3463         # set the series flag
3464     my $serial = 0;
3465     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
3466
3467         my $query = 
3468         "INSERT INTO biblio
3469                 SET frameworkcode = ?,
3470                         author = ?,
3471                         title = ?,
3472                         unititle =?,
3473                         notes = ?,
3474                         serial = ?,
3475                         seriestitle = ?,
3476                         copyrightdate = ?,
3477                         datecreated=NOW(),
3478                         abstract = ?
3479                 ";
3480     my $sth = $dbh->prepare($query);
3481     $sth->execute(
3482                 $frameworkcode,
3483         $biblio->{'author'},
3484         $biblio->{'title'},
3485                 $biblio->{'unititle'},
3486         $biblio->{'notes'},
3487                 $serial,
3488         $biblio->{'seriestitle'},
3489                 $biblio->{'copyrightdate'},
3490         $biblio->{'abstract'}
3491     );
3492
3493     my $biblionumber = $dbh->{'mysql_insertid'};
3494         if ( $dbh->errstr ) {
3495                 $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
3496         warn $error;
3497     }
3498
3499     $sth->finish();
3500         #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3501     return ($biblionumber,$error);
3502 }
3503
3504 =head2 _koha_modify_biblio
3505
3506 =over 4
3507
3508 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3509
3510 Internal function for updating the biblio table
3511
3512 =back
3513
3514 =cut
3515
3516 sub _koha_modify_biblio {
3517     my ( $dbh, $biblio, $frameworkcode ) = @_;
3518         my $error;
3519
3520     my $query = "
3521         UPDATE biblio
3522         SET    frameworkcode = ?,
3523                            author = ?,
3524                            title = ?,
3525                            unititle = ?,
3526                            notes = ?,
3527                            serial = ?,
3528                            seriestitle = ?,
3529                            copyrightdate = ?,
3530                abstract = ?
3531         WHERE  biblionumber = ?
3532                 "
3533         ;
3534     my $sth = $dbh->prepare($query);
3535     
3536     $sth->execute(
3537                 $frameworkcode,
3538         $biblio->{'author'},
3539         $biblio->{'title'},
3540         $biblio->{'unititle'},
3541         $biblio->{'notes'},
3542         $biblio->{'serial'},
3543         $biblio->{'seriestitle'},
3544         $biblio->{'copyrightdate'},
3545                 $biblio->{'abstract'},
3546         $biblio->{'biblionumber'}
3547     ) if $biblio->{'biblionumber'};
3548
3549     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3550                 $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
3551         warn $error;
3552     }
3553     return ( $biblio->{'biblionumber'},$error );
3554 }
3555
3556 =head2 _koha_modify_biblioitem_nonmarc
3557
3558 =over 4
3559
3560 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3561
3562 Updates biblioitems row except for marc and marcxml, which should be changed
3563 via ModBiblioMarc
3564
3565 =back
3566
3567 =cut
3568
3569 sub _koha_modify_biblioitem_nonmarc {
3570     my ( $dbh, $biblioitem ) = @_;
3571         my $error;
3572
3573         # re-calculate the cn_sort, it may have changed
3574         my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3575
3576         my $query = 
3577         "UPDATE biblioitems 
3578         SET biblionumber        = ?,
3579                 volume                  = ?,
3580                 number                  = ?,
3581         itemtype        = ?,
3582         isbn            = ?,
3583         issn            = ?,
3584                 publicationyear = ?,
3585         publishercode   = ?,
3586                 volumedate      = ?,
3587                 volumedesc      = ?,
3588                 collectiontitle = ?,
3589                 collectionissn  = ?,
3590                 collectionvolume= ?,
3591                 editionstatement= ?,
3592                 editionresponsibility = ?,
3593                 illus                   = ?,
3594                 pages                   = ?,
3595                 notes                   = ?,
3596                 size                    = ?,
3597                 place                   = ?,
3598                 lccn                    = ?,
3599                 url                     = ?,
3600         cn_source               = ?,
3601         cn_class        = ?,
3602         cn_item         = ?,
3603                 cn_suffix       = ?,
3604                 cn_sort         = ?,
3605                 totalissues     = ?
3606         where biblioitemnumber = ?
3607                 ";
3608         my $sth = $dbh->prepare($query);
3609         $sth->execute(
3610                 $biblioitem->{'biblionumber'},
3611                 $biblioitem->{'volume'},
3612                 $biblioitem->{'number'},
3613                 $biblioitem->{'itemtype'},
3614                 $biblioitem->{'isbn'},
3615                 $biblioitem->{'issn'},
3616                 $biblioitem->{'publicationyear'},
3617                 $biblioitem->{'publishercode'},
3618                 $biblioitem->{'volumedate'},
3619                 $biblioitem->{'volumedesc'},
3620                 $biblioitem->{'collectiontitle'},
3621                 $biblioitem->{'collectionissn'},
3622                 $biblioitem->{'collectionvolume'},
3623                 $biblioitem->{'editionstatement'},
3624                 $biblioitem->{'editionresponsibility'},
3625                 $biblioitem->{'illus'},
3626                 $biblioitem->{'pages'},
3627                 $biblioitem->{'bnotes'},
3628                 $biblioitem->{'size'},
3629                 $biblioitem->{'place'},
3630                 $biblioitem->{'lccn'},
3631                 $biblioitem->{'url'},
3632                 $biblioitem->{'biblioitems.cn_source'},
3633                 $biblioitem->{'cn_class'},
3634                 $biblioitem->{'cn_item'},
3635                 $biblioitem->{'cn_suffix'},
3636                 $cn_sort,
3637                 $biblioitem->{'totalissues'},
3638                 $biblioitem->{'biblioitemnumber'}
3639         );
3640     if ( $dbh->errstr ) {
3641                 $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
3642         warn $error;
3643     }
3644         return ($biblioitem->{'biblioitemnumber'},$error);
3645 }
3646
3647 =head2 _koha_add_biblioitem
3648
3649 =over 4
3650
3651 my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3652
3653 Internal function to add a biblioitem
3654
3655 =back
3656
3657 =cut
3658
3659 sub _koha_add_biblioitem {
3660     my ( $dbh, $biblioitem ) = @_;
3661         my $error;
3662
3663         my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3664     my $query =
3665     "INSERT INTO biblioitems SET
3666         biblionumber    = ?,
3667         volume          = ?,
3668         number          = ?,
3669         itemtype        = ?,
3670         isbn            = ?,
3671         issn            = ?,
3672         publicationyear = ?,
3673         publishercode   = ?,
3674         volumedate      = ?,
3675         volumedesc      = ?,
3676         collectiontitle = ?,
3677         collectionissn  = ?,
3678         collectionvolume= ?,
3679         editionstatement= ?,
3680         editionresponsibility = ?,
3681         illus           = ?,
3682         pages           = ?,
3683         notes           = ?,
3684         size            = ?,
3685         place           = ?,
3686         lccn            = ?,
3687         marc            = ?,
3688         url             = ?,
3689         cn_source       = ?,
3690         cn_class        = ?,
3691         cn_item         = ?,
3692         cn_suffix       = ?,
3693         cn_sort         = ?,
3694         totalissues     = ?
3695         ";
3696         my $sth = $dbh->prepare($query);
3697     $sth->execute(
3698         $biblioitem->{'biblionumber'},
3699         $biblioitem->{'volume'},
3700         $biblioitem->{'number'},
3701         $biblioitem->{'itemtype'},
3702         $biblioitem->{'isbn'},
3703         $biblioitem->{'issn'},
3704         $biblioitem->{'publicationyear'},
3705         $biblioitem->{'publishercode'},
3706         $biblioitem->{'volumedate'},
3707         $biblioitem->{'volumedesc'},
3708         $biblioitem->{'collectiontitle'},
3709         $biblioitem->{'collectionissn'},
3710         $biblioitem->{'collectionvolume'},
3711         $biblioitem->{'editionstatement'},
3712         $biblioitem->{'editionresponsibility'},
3713         $biblioitem->{'illus'},
3714         $biblioitem->{'pages'},
3715         $biblioitem->{'bnotes'},
3716         $biblioitem->{'size'},
3717         $biblioitem->{'place'},
3718         $biblioitem->{'lccn'},
3719         $biblioitem->{'marc'},
3720         $biblioitem->{'url'},
3721         $biblioitem->{'biblioitems.cn_source'},
3722         $biblioitem->{'cn_class'},
3723         $biblioitem->{'cn_item'},
3724         $biblioitem->{'cn_suffix'},
3725         $cn_sort,
3726         $biblioitem->{'totalissues'}
3727     );
3728     my $bibitemnum = $dbh->{'mysql_insertid'};
3729     if ( $dbh->errstr ) {
3730                 $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
3731                 warn $error;
3732     }
3733     $sth->finish();
3734     return ($bibitemnum,$error);
3735 }
3736
3737 =head2 _koha_new_items
3738
3739 =over 4
3740
3741 my ($itemnumber,$error) = _koha_new_items( $dbh, $item, $barcode );
3742
3743 =back
3744
3745 =cut
3746
3747 sub _koha_new_items {
3748     my ( $dbh, $item, $barcode ) = @_;
3749         my $error;
3750
3751     my ($items_cn_sort) = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
3752
3753     # if dateaccessioned is provided, use it. Otherwise, set to NOW()
3754     if ( $item->{'dateaccessioned'} eq '' || !$item->{'dateaccessioned'} ) {
3755                 my $today = C4::Dates->new();    
3756                 $item->{'dateaccessioned'} =  $today->output("iso"); #TODO: check time issues
3757         }
3758         my $query = 
3759            "INSERT INTO items SET
3760                         biblionumber            = ?,
3761             biblioitemnumber    = ?,
3762                         barcode                 = ?,
3763                         dateaccessioned         = ?,
3764                         booksellerid        = ?,
3765             homebranch          = ?,
3766             price               = ?,
3767                         replacementprice        = ?,
3768             replacementpricedate = NOW(),
3769                         datelastborrowed        = ?,
3770                         datelastseen            = NOW(),
3771                         stack                   = ?,
3772                         notforloan                      = ?,
3773                         damaged                         = ?,
3774             itemlost            = ?,
3775                         wthdrawn                = ?,
3776                         itemcallnumber          = ?,
3777                         restricted                      = ?,
3778                         itemnotes                       = ?,
3779                         holdingbranch           = ?,
3780             paidfor             = ?,
3781                         location                        = ?,
3782                         onloan                          = ?,
3783                         cn_source                       = ?,
3784                         cn_sort                         = ?,
3785                         ccode                           = ?,
3786                         materials                       = ?,
3787                         uri                             = ?
3788           ";
3789     my $sth = $dbh->prepare($query);
3790         $sth->execute(
3791                         $item->{'biblionumber'},
3792                         $item->{'biblioitemnumber'},
3793             $barcode,
3794                         $item->{'dateaccessioned'},
3795                         $item->{'booksellerid'},
3796             $item->{'homebranch'},
3797             $item->{'price'},
3798                         $item->{'replacementprice'},
3799                         $item->{datelastborrowed},
3800                         $item->{stack},
3801                         $item->{'notforloan'},
3802                         $item->{'damaged'},
3803             $item->{'itemlost'},
3804                         $item->{'wthdrawn'},
3805                         $item->{'itemcallnumber'},
3806             $item->{'restricted'},
3807                         $item->{'itemnotes'},
3808                         $item->{'holdingbranch'},
3809                         $item->{'paidfor'},
3810                         $item->{'location'},
3811                         $item->{'onloan'},
3812                         $item->{'items.cn_source'},
3813                         $items_cn_sort,
3814                         $item->{'ccode'},
3815                         $item->{'materials'},
3816                         $item->{'uri'},
3817     );
3818     my $itemnumber = $dbh->{'mysql_insertid'};
3819     if ( defined $sth->errstr ) {
3820         $error.="ERROR in _koha_new_items $query".$sth->errstr;
3821     }
3822         $sth->finish();
3823     return ( $itemnumber, $error );
3824 }
3825
3826 =head2 _koha_modify_item
3827
3828 =over 4
3829
3830 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
3831
3832 =back
3833
3834 =cut
3835
3836 sub _koha_modify_item {
3837     my ( $dbh, $item ) = @_;
3838         my $error;
3839
3840         # calculate items.cn_sort
3841     $item->{'cn_sort'} = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
3842
3843     my $query = "UPDATE items SET ";
3844         my @bind;
3845         for my $key ( keys %$item ) {
3846                 $query.="$key=?,";
3847                 push @bind, $item->{$key};
3848     }
3849         $query =~ s/,$//;
3850     $query .= " WHERE itemnumber=?";
3851     push @bind, $item->{'itemnumber'};
3852     my $sth = $dbh->prepare($query);
3853     $sth->execute(@bind);
3854     if ( $dbh->errstr ) {
3855         $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
3856         warn $error;
3857     }
3858     $sth->finish();
3859         return ($item->{'itemnumber'},$error);
3860 }
3861
3862 =head2 _koha_delete_biblio
3863
3864 =over 4
3865
3866 $error = _koha_delete_biblio($dbh,$biblionumber);
3867
3868 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3869
3870 C<$dbh> - the database handle
3871 C<$biblionumber> - the biblionumber of the biblio to be deleted
3872
3873 =back
3874
3875 =cut
3876
3877 # FIXME: add error handling
3878
3879 sub _koha_delete_biblio {
3880     my ( $dbh, $biblionumber ) = @_;
3881
3882     # get all the data for this biblio
3883     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3884     $sth->execute($biblionumber);
3885
3886     if ( my $data = $sth->fetchrow_hashref ) {
3887
3888         # save the record in deletedbiblio
3889         # find the fields to save
3890         my $query = "INSERT INTO deletedbiblio SET ";
3891         my @bind  = ();
3892         foreach my $temp ( keys %$data ) {
3893             $query .= "$temp = ?,";
3894             push( @bind, $data->{$temp} );
3895         }
3896
3897         # replace the last , by ",?)"
3898         $query =~ s/\,$//;
3899         my $bkup_sth = $dbh->prepare($query);
3900         $bkup_sth->execute(@bind);
3901         $bkup_sth->finish;
3902
3903         # delete the biblio
3904         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3905         $del_sth->execute($biblionumber);
3906         $del_sth->finish;
3907     }
3908     $sth->finish;
3909     return undef;
3910 }
3911
3912 =head2 _koha_delete_biblioitems
3913
3914 =over 4
3915
3916 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3917
3918 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3919
3920 C<$dbh> - the database handle
3921 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3922
3923 =back
3924
3925 =cut
3926
3927 # FIXME: add error handling
3928
3929 sub _koha_delete_biblioitems {
3930     my ( $dbh, $biblioitemnumber ) = @_;
3931
3932     # get all the data for this biblioitem
3933     my $sth =
3934       $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3935     $sth->execute($biblioitemnumber);
3936
3937     if ( my $data = $sth->fetchrow_hashref ) {
3938
3939         # save the record in deletedbiblioitems
3940         # find the fields to save
3941         my $query = "INSERT INTO deletedbiblioitems SET ";
3942         my @bind  = ();
3943         foreach my $temp ( keys %$data ) {
3944             $query .= "$temp = ?,";
3945             push( @bind, $data->{$temp} );
3946         }
3947
3948         # replace the last , by ",?)"
3949         $query =~ s/\,$//;
3950         my $bkup_sth = $dbh->prepare($query);
3951         $bkup_sth->execute(@bind);
3952         $bkup_sth->finish;
3953
3954         # delete the biblioitem
3955         my $del_sth =
3956           $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3957         $del_sth->execute($biblioitemnumber);
3958         $del_sth->finish;
3959     }
3960     $sth->finish;
3961     return undef;
3962 }
3963
3964 =head2 _koha_delete_item
3965
3966 =over 4
3967
3968 _koha_delete_item( $dbh, $itemnum );
3969
3970 Internal function to delete an item record from the koha tables
3971
3972 =back
3973
3974 =cut
3975
3976 sub _koha_delete_item {
3977     my ( $dbh, $itemnum ) = @_;
3978
3979         # save the deleted item to deleteditems table
3980     my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
3981     $sth->execute($itemnum);
3982     my $data = $sth->fetchrow_hashref();
3983     $sth->finish();
3984     my $query = "INSERT INTO deleteditems SET ";
3985     my @bind  = ();
3986     foreach my $key ( keys %$data ) {
3987         $query .= "$key = ?,";
3988         push( @bind, $data->{$key} );
3989     }
3990     $query =~ s/\,$//;
3991     $sth = $dbh->prepare($query);
3992     $sth->execute(@bind);
3993     $sth->finish();
3994
3995         # delete from items table
3996     $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
3997     $sth->execute($itemnum);
3998     $sth->finish();
3999         return undef;
4000 }
4001
4002 =head1 UNEXPORTED FUNCTIONS
4003
4004 =head2 ModBiblioMarc
4005
4006     &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
4007     
4008     Add MARC data for a biblio to koha 
4009     
4010     Function exported, but should NOT be used, unless you really know what you're doing
4011
4012 =cut
4013
4014 sub ModBiblioMarc {
4015     
4016 # pass the MARC::Record to this function, and it will create the records in the marc field
4017     my ( $record, $biblionumber, $frameworkcode ) = @_;
4018     my $dbh = C4::Context->dbh;
4019     my @fields = $record->fields();
4020     if ( !$frameworkcode ) {
4021         $frameworkcode = "";
4022     }
4023     my $sth =
4024       $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
4025     $sth->execute( $frameworkcode, $biblionumber );
4026     $sth->finish;
4027     my $encoding = C4::Context->preference("marcflavour");
4028
4029     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
4030     if ( $encoding eq "UNIMARC" ) {
4031         my $string;
4032         if ( length($record->subfield( 100, "a" )) == 35 ) {
4033             $string = $record->subfield( 100, "a" );
4034             my $f100 = $record->field(100);
4035             $record->delete_field($f100);
4036         }
4037         else {
4038             $string = POSIX::strftime( "%Y%m%d", localtime );
4039             $string =~ s/\-//g;
4040             $string = sprintf( "%-*s", 35, $string );
4041         }
4042         substr( $string, 22, 6, "frey50" );
4043         unless ( $record->subfield( 100, "a" ) ) {
4044             $record->insert_grouped_field(
4045                 MARC::Field->new( 100, "", "", "a" => $string ) );
4046         }
4047     }
4048     ModZebra($biblionumber,"specialUpdate","biblioserver",$record);
4049     $sth =
4050       $dbh->prepare(
4051         "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
4052     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
4053         $biblionumber );
4054     $sth->finish;
4055     return $biblionumber;
4056 }
4057
4058 =head2 AddItemInMarc
4059
4060 =over 4
4061
4062 $newbiblionumber = AddItemInMarc( $record, $biblionumber, $frameworkcode );
4063
4064 Add an item in a MARC record and save the MARC record
4065
4066 Function exported, but should NOT be used, unless you really know what you're doing
4067
4068 =back
4069
4070 =cut
4071
4072 sub AddItemInMarc {
4073
4074     # pass the MARC::Record to this function, and it will create the records in the marc tables
4075     my ( $record, $biblionumber, $frameworkcode ) = @_;
4076     my $newrec = &GetMarcBiblio($biblionumber);
4077
4078     # create it
4079     my @fields = $record->fields();
4080     foreach my $field (@fields) {
4081         $newrec->append_fields($field);
4082     }
4083
4084     # FIXME: should we be making sure the biblionumbers are the same?
4085     my $newbiblionumber =
4086       &ModBiblioMarc( $newrec, $biblionumber, $frameworkcode );
4087     return $newbiblionumber;
4088 }
4089
4090 =head2 z3950_extended_services
4091
4092 z3950_extended_services($serviceType,$serviceOptions,$record);
4093
4094     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.
4095
4096 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
4097
4098 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
4099
4100     action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
4101
4102 and maybe
4103
4104     recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
4105     syntax => the record syntax (transfer syntax)
4106     databaseName = Database from connection object
4107
4108     To set serviceOptions, call set_service_options($serviceType)
4109
4110 C<$record> the record, if one is needed for the service type
4111
4112     A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
4113
4114 =cut
4115
4116 sub z3950_extended_services {
4117     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
4118
4119     # get our connection object
4120     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
4121
4122     # create a new package object
4123     my $Zpackage = $Zconn->package();
4124
4125     # set our options
4126     $Zpackage->option( action => $action );
4127
4128     if ( $serviceOptions->{'databaseName'} ) {
4129         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
4130     }
4131     if ( $serviceOptions->{'recordIdNumber'} ) {
4132         $Zpackage->option(
4133             recordIdNumber => $serviceOptions->{'recordIdNumber'} );
4134     }
4135     if ( $serviceOptions->{'recordIdOpaque'} ) {
4136         $Zpackage->option(
4137             recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
4138     }
4139
4140  # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
4141  #if ($serviceType eq 'itemorder') {
4142  #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
4143  #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
4144  #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
4145  #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
4146  #}
4147
4148     if ( $serviceOptions->{record} ) {
4149         $Zpackage->option( record => $serviceOptions->{record} );
4150
4151         # can be xml or marc
4152         if ( $serviceOptions->{'syntax'} ) {
4153             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
4154         }
4155     }
4156
4157     # send the request, handle any exception encountered
4158     eval { $Zpackage->send($serviceType) };
4159     if ( $@ && $@->isa("ZOOM::Exception") ) {
4160         return "error:  " . $@->code() . " " . $@->message() . "\n";
4161     }
4162
4163     # free up package resources
4164     $Zpackage->destroy();
4165 }
4166
4167 =head2 set_service_options
4168
4169 my $serviceOptions = set_service_options($serviceType);
4170
4171 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
4172
4173 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
4174
4175 =cut
4176
4177 sub set_service_options {
4178     my ($serviceType) = @_;
4179     my $serviceOptions;
4180
4181 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
4182 #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
4183
4184     if ( $serviceType eq 'commit' ) {
4185
4186         # nothing to do
4187     }
4188     if ( $serviceType eq 'create' ) {
4189
4190         # nothing to do
4191     }
4192     if ( $serviceType eq 'drop' ) {
4193         die "ERROR: 'drop' not currently supported (by Zebra)";
4194     }
4195     return $serviceOptions;
4196 }
4197
4198 =head2 GetItemsCount
4199
4200 $count = &GetItemsCount( $biblionumber);
4201 this function return count of item with $biblionumber
4202 =cut
4203
4204 sub GetItemsCount {
4205     my ( $biblionumber ) = @_;
4206     my $dbh = C4::Context->dbh;
4207     my $query = "SELECT count(*)
4208                   FROM  items 
4209                   WHERE biblionumber=?";
4210     my $sth = $dbh->prepare($query);
4211     $sth->execute($biblionumber);
4212     my $count = $sth->fetchrow;  
4213     $sth->finish;
4214     return ($count);
4215 }
4216
4217 END { }    # module clean-up code here (global destructor)
4218
4219 1;
4220
4221 __END__
4222
4223 =head1 AUTHOR
4224
4225 Koha Developement team <info@koha.org>
4226
4227 Paul POULAIN paul.poulain@free.fr
4228
4229 Joshua Ferraro jmf@liblime.com
4230
4231 =cut