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