Biblio.pm - BEGIN block VERSION and vars related to export.
[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 # use utf8;
22 use MARC::Record;
23 use MARC::File::USMARC;
24 use MARC::File::XML;
25 use ZOOM;
26
27 use C4::Context;
28 use C4::Koha;
29 use C4::Branch;
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 BEGIN {
37         $VERSION = 1.00;
38
39         require Exporter;
40         @ISA = qw( Exporter );
41
42         # to add biblios
43         push @EXPORT, qw( 
44                 &AddBiblio
45         );
46
47         # to get something
48         push @EXPORT, qw(
49                 &GetBiblio
50                 &GetBiblioData
51                 &GetBiblioItemData
52                 &GetBiblioItemInfosOf
53                 &GetBiblioItemByBiblioNumber
54                 &GetBiblioFromItemNumber
55
56                 &GetMarcNotes
57                 &GetMarcSubjects
58                 &GetMarcBiblio
59                 &GetMarcAuthors
60                 &GetMarcSeries
61                 GetMarcUrls
62                 &GetUsedMarcStructure
63
64                 &GetXmlBiblio
65
66                 &GetAuthorisedValueDesc
67                 &GetMarcStructure
68                 &GetMarcFromKohaField
69                 &GetFrameworkCode
70                 &GetPublisherNameFromIsbn
71                 &TransformKohaToMarc
72         );
73
74         # To modify something
75         push @EXPORT, qw(
76                 &ModBiblio
77                 &ModBiblioframework
78                 &ModZebra
79         );
80
81         # To delete something
82         push @EXPORT, qw(
83                 &DelBiblio
84         );
85
86         # Internal functions
87         # those functions are exported but should not be used
88         # they are usefull is few circumstances, so are exported.
89         # but don't use them unless you're a core developer ;-)
90         push @EXPORT, qw(
91                 &ModBiblioMarc
92         );
93
94         # Others functions
95         push @EXPORT, qw(
96                 &TransformMarcToKoha
97                 &TransformHtmlToMarc2
98                 &TransformHtmlToMarc
99                 &TransformHtmlToXml
100                 &PrepareItemrecordDisplay
101                 &char_decode
102                 &GetNoZebraIndexes
103         );
104 }
105
106 =head1 NAME
107
108 C4::Biblio - cataloging management functions
109
110 =head1 DESCRIPTION
111
112 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:
113
114 =over 4
115
116 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
117
118 =item 2. as raw MARC in the Zebra index and storage engine
119
120 =item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
121
122 =back
123
124 In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
125
126 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.
127
128 =over 4
129
130 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
131
132 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
133
134 =back
135
136 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:
137
138 =over 4
139
140 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
141
142 =item 2. _koha_* - low-level internal functions for managing the koha tables
143
144 =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.
145
146 =item 4. Zebra functions used to update the Zebra index
147
148 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
149
150 =back
151
152 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 :
153
154 =over 4
155
156 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
157
158 =item 2. add the biblionumber and biblioitemnumber into the MARC records
159
160 =item 3. save the marc record
161
162 =back
163
164 When dealing with items, we must :
165
166 =over 4
167
168 =item 1. save the item in items table, that gives us an itemnumber
169
170 =item 2. add the itemnumber to the item MARC field
171
172 =item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
173
174 When modifying a biblio or an item, the behaviour is quite similar.
175
176 =back
177
178 =head1 EXPORTED FUNCTIONS
179
180 =head2 AddBiblio
181
182 =over 4
183
184 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
185
186 =back
187
188 Exported function (core API) for adding a new biblio to koha.
189
190 The first argument is a C<MARC::Record> object containing the
191 bib to add, while the second argument is the desired MARC
192 framework code.
193
194 This function also accepts a third, optional argument: a hashref
195 to additional options.  The only defined option is C<defer_marc_save>,
196 which if present and mapped to a true value, causes C<AddBiblio>
197 to omit the call to save the MARC in C<bibilioitems.marc>
198 and C<biblioitems.marcxml>  This option is provided B<only>
199 for the use of scripts such as C<bulkmarcimport.pl> that may need
200 to do some manipulation of the MARC record for item parsing before
201 saving it and which cannot afford the performance hit of saving
202 the MARC record twice.  Consequently, do not use that option
203 unless you can guarantee that C<ModBiblioMarc> will be called.
204
205 =cut
206
207 sub AddBiblio {
208     my $record = shift;
209     my $frameworkcode = shift;
210     my $options = @_ ? shift : undef;
211     my $defer_marc_save = 0;
212     if (defined $options and exists $options->{'defer_marc_save'} and $options->{'defer_marc_save'}) {
213         $defer_marc_save = 1;
214     }
215
216     my ($biblionumber,$biblioitemnumber,$error);
217     my $dbh = C4::Context->dbh;
218     # transform the data into koha-table style data
219     my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
220     ($biblionumber,$error) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
221     $olddata->{'biblionumber'} = $biblionumber;
222     ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $olddata );
223
224     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
225
226     # now add the record
227     $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode ) unless $defer_marc_save;
228       
229     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio") 
230         if C4::Context->preference("CataloguingLog");
231
232     return ( $biblionumber, $biblioitemnumber );
233 }
234
235 =head2 ModBiblio
236
237     ModBiblio( $record,$biblionumber,$frameworkcode);
238     Exported function (core API) to modify a biblio
239
240 =cut
241
242 sub ModBiblio {
243     my ( $record, $biblionumber, $frameworkcode ) = @_;
244     if (C4::Context->preference("CataloguingLog")) {
245         my $newrecord = GetMarcBiblio($biblionumber);
246         &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,"BEFORE=>".$newrecord->as_formatted);
247     }
248     
249     my $dbh = C4::Context->dbh;
250     
251     $frameworkcode = "" unless $frameworkcode;
252
253     # get the items before and append them to the biblio before updating the record, atm we just have the biblio
254     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
255     my $oldRecord = GetMarcBiblio( $biblionumber );
256     
257     # parse each item, and, for an unknown reason, re-encode each subfield 
258     # if you don't do that, the record will have encoding mixed
259     # and the biblio will be re-encoded.
260     # strange, I (Paul P.) searched more than 1 day to understand what happends
261     # but could only solve the problem this way...
262    my @fields = $oldRecord->field( $itemtag );
263     foreach my $fielditem ( @fields ){
264         my $field;
265         foreach ($fielditem->subfields()) {
266             if ($field) {
267                 $field->add_subfields(Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
268             } else {
269                 $field = MARC::Field->new("$itemtag",'','',Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
270             }
271           }
272         $record->append_fields($field);
273     }
274     
275     # update biblionumber and biblioitemnumber in MARC
276     # FIXME - this is assuming a 1 to 1 relationship between
277     # biblios and biblioitems
278     my $sth =  $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
279     $sth->execute($biblionumber);
280     my ($biblioitemnumber) = $sth->fetchrow;
281     $sth->finish();
282     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
283
284     # update the MARC record (that now contains biblio and items) with the new record data
285     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
286     
287     # load the koha-table data object
288     my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
289
290     # modify the other koha tables
291     _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
292     _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
293     return 1;
294 }
295
296 =head2 ModBiblioframework
297
298     ModBiblioframework($biblionumber,$frameworkcode);
299     Exported function to modify a biblio framework
300
301 =cut
302
303 sub ModBiblioframework {
304     my ( $biblionumber, $frameworkcode ) = @_;
305     my $dbh = C4::Context->dbh;
306     my $sth = $dbh->prepare(
307         "UPDATE biblio SET frameworkcode=? WHERE biblionumber=?"
308     );
309     $sth->execute($frameworkcode, $biblionumber);
310     return 1;
311 }
312
313 =head2 DelBiblio
314
315 =over
316
317 my $error = &DelBiblio($dbh,$biblionumber);
318 Exported function (core API) for deleting a biblio in koha.
319 Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)
320 Also backs it up to deleted* tables
321 Checks to make sure there are not issues on any of the items
322 return:
323 C<$error> : undef unless an error occurs
324
325 =back
326
327 =cut
328
329 sub DelBiblio {
330     my ( $biblionumber ) = @_;
331     my $dbh = C4::Context->dbh;
332     my $error;    # for error handling
333     
334     # First make sure this biblio has no items attached
335     my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
336     $sth->execute($biblionumber);
337     if (my $itemnumber = $sth->fetchrow){
338         # Fix this to use a status the template can understand
339         $error .= "This Biblio has items attached, please delete them first before deleting this biblio ";
340     }
341
342     return $error if $error;
343
344     # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
345     # for at least 2 reasons :
346     # - we need to read the biblio if NoZebra is set (to remove it from the indexes
347     # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
348     #   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)
349     ModZebra($biblionumber, "recordDelete", "biblioserver", undef);
350
351     # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
352     $sth =
353       $dbh->prepare(
354         "SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
355     $sth->execute($biblionumber);
356     while ( my $biblioitemnumber = $sth->fetchrow ) {
357
358         # delete this biblioitem
359         $error = _koha_delete_biblioitems( $dbh, $biblioitemnumber );
360         return $error if $error;
361     }
362
363     # delete biblio from Koha tables and save in deletedbiblio
364     # must do this *after* _koha_delete_biblioitems, otherwise
365     # delete cascade will prevent deletedbiblioitems rows
366     # from being generated by _koha_delete_biblioitems
367     $error = _koha_delete_biblio( $dbh, $biblionumber );
368
369     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$biblionumber,"") 
370         if C4::Context->preference("CataloguingLog");
371     return;
372 }
373
374 =head2 GetBiblioData
375
376 =over 4
377
378 $data = &GetBiblioData($biblionumber);
379 Returns information about the book with the given biblionumber.
380 C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
381 the C<biblio> and C<biblioitems> tables in the
382 Koha database.
383 In addition, C<$data-E<gt>{subject}> is the list of the book's
384 subjects, separated by C<" , "> (space, comma, space).
385 If there are multiple biblioitems with the given biblionumber, only
386 the first one is considered.
387
388 =back
389
390 =cut
391
392 sub GetBiblioData {
393     my ( $bibnum ) = @_;
394     my $dbh = C4::Context->dbh;
395
396   #  my $query =  C4::Context->preference('item-level_itypes') ? 
397     #   " SELECT * , biblioitems.notes AS bnotes, biblio.notes
398     #       FROM biblio
399     #        LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
400     #       WHERE biblio.biblionumber = ?
401     #        AND biblioitems.biblionumber = biblio.biblionumber
402     #";
403     
404     my $query = " SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
405             FROM biblio
406             LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
407             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
408             WHERE biblio.biblionumber = ?
409             AND biblioitems.biblionumber = biblio.biblionumber ";
410          
411     my $sth = $dbh->prepare($query);
412     $sth->execute($bibnum);
413     my $data;
414     $data = $sth->fetchrow_hashref;
415     $sth->finish;
416
417     return ($data);
418 }    # sub GetBiblioData
419
420 =head2 &GetBiblioItemData
421
422 =over 4
423
424 $itemdata = &GetBiblioItemData($biblioitemnumber);
425
426 Looks up the biblioitem with the given biblioitemnumber. Returns a
427 reference-to-hash. The keys are the fields from the C<biblio>,
428 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
429 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
430
431 =back
432
433 =cut
434
435 #'
436 sub GetBiblioItemData {
437     my ($biblioitemnumber) = @_;
438     my $dbh       = C4::Context->dbh;
439     my $query = "SELECT *,biblioitems.notes AS bnotes
440         FROM biblio, biblioitems ";
441     unless(C4::Context->preference('item-level_itypes')) { 
442         $query .= "LEFT JOIN itemtypes on biblioitems.itemtype=itemtypes.itemtype ";
443     }    
444     $query .= " WHERE biblio.biblionumber = biblioitems.biblionumber 
445         AND biblioitemnumber = ? ";
446     my $sth       =  $dbh->prepare($query);
447     my $data;
448     $sth->execute($biblioitemnumber);
449     $data = $sth->fetchrow_hashref;
450     $sth->finish;
451     return ($data);
452 }    # sub &GetBiblioItemData
453
454 =head2 GetBiblioItemByBiblioNumber
455
456 =over 4
457
458 NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.
459
460 =back
461
462 =cut
463
464 sub GetBiblioItemByBiblioNumber {
465     my ($biblionumber) = @_;
466     my $dbh = C4::Context->dbh;
467     my $sth = $dbh->prepare("Select * FROM biblioitems WHERE biblionumber = ?");
468     my $count = 0;
469     my @results;
470
471     $sth->execute($biblionumber);
472
473     while ( my $data = $sth->fetchrow_hashref ) {
474         push @results, $data;
475     }
476
477     $sth->finish;
478     return @results;
479 }
480
481 =head2 GetBiblioFromItemNumber
482
483 =over 4
484
485 $item = &GetBiblioFromItemNumber($itemnumber,$barcode);
486
487 Looks up the item with the given itemnumber. if undef, try the barcode.
488
489 C<&itemnodata> returns a reference-to-hash whose keys are the fields
490 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
491 database.
492
493 =back
494
495 =cut
496
497 #'
498 sub GetBiblioFromItemNumber {
499     my ( $itemnumber, $barcode ) = @_;
500     my $dbh = C4::Context->dbh;
501     my $sth;
502     if($itemnumber) {
503         $sth=$dbh->prepare(  "SELECT * FROM items 
504             LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
505             LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
506              WHERE items.itemnumber = ?") ; 
507         $sth->execute($itemnumber);
508     } else {
509         $sth=$dbh->prepare(  "SELECT * FROM items 
510             LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
511             LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
512              WHERE items.barcode = ?") ; 
513         $sth->execute($barcode);
514     }
515     my $data = $sth->fetchrow_hashref;
516     $sth->finish;
517     return ($data);
518 }
519
520 =head2 GetBiblio
521
522 =over 4
523
524 ( $count, @results ) = &GetBiblio($biblionumber);
525
526 =back
527
528 =cut
529
530 sub GetBiblio {
531     my ($biblionumber) = @_;
532     my $dbh = C4::Context->dbh;
533     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber = ?");
534     my $count = 0;
535     my @results;
536     $sth->execute($biblionumber);
537     while ( my $data = $sth->fetchrow_hashref ) {
538         $results[$count] = $data;
539         $count++;
540     }    # while
541     $sth->finish;
542     return ( $count, @results );
543 }    # sub GetBiblio
544
545 =head2 GetBiblioItemInfosOf
546
547 =over 4
548
549 GetBiblioItemInfosOf(@biblioitemnumbers);
550
551 =back
552
553 =cut
554
555 sub GetBiblioItemInfosOf {
556     my @biblioitemnumbers = @_;
557
558     my $query = '
559         SELECT biblioitemnumber,
560             publicationyear,
561             itemtype
562         FROM biblioitems
563         WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
564     ';
565     return get_infos_of( $query, 'biblioitemnumber' );
566 }
567
568 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
569
570 =head2 GetMarcStructure
571
572 =over 4
573
574 $res = GetMarcStructure($forlibrarian,$frameworkcode);
575
576 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
577 $forlibrarian  :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
578 $frameworkcode : the framework code to read
579
580 =back
581
582 =cut
583
584 sub GetMarcStructure {
585     my ( $forlibrarian, $frameworkcode ) = @_;
586     my $dbh=C4::Context->dbh;
587     $frameworkcode = "" unless $frameworkcode;
588     my $sth;
589     my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';
590
591     # check that framework exists
592     $sth =
593       $dbh->prepare(
594         "SELECT COUNT(*) FROM marc_tag_structure WHERE frameworkcode=?");
595     $sth->execute($frameworkcode);
596     my ($total) = $sth->fetchrow;
597     $frameworkcode = "" unless ( $total > 0 );
598     $sth =
599       $dbh->prepare(
600         "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable 
601         FROM marc_tag_structure 
602         WHERE frameworkcode=? 
603         ORDER BY tagfield"
604       );
605     $sth->execute($frameworkcode);
606     my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
607
608     while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) =
609         $sth->fetchrow )
610     {
611         $res->{$tag}->{lib} =
612           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
613         $res->{$tab}->{tab}        = "";
614         $res->{$tag}->{mandatory}  = $mandatory;
615         $res->{$tag}->{repeatable} = $repeatable;
616     }
617
618     $sth =
619       $dbh->prepare(
620             "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue 
621                 FROM marc_subfield_structure 
622             WHERE frameworkcode=? 
623                 ORDER BY tagfield,tagsubfield
624             "
625     );
626     
627     $sth->execute($frameworkcode);
628
629     my $subfield;
630     my $authorised_value;
631     my $authtypecode;
632     my $value_builder;
633     my $kohafield;
634     my $seealso;
635     my $hidden;
636     my $isurl;
637     my $link;
638     my $defaultvalue;
639
640     while (
641         (
642             $tag,          $subfield,      $liblibrarian,
643             ,              $libopac,       $tab,
644             $mandatory,    $repeatable,    $authorised_value,
645             $authtypecode, $value_builder, $kohafield,
646             $seealso,      $hidden,        $isurl,
647             $link,$defaultvalue
648         )
649         = $sth->fetchrow
650       )
651     {
652         $res->{$tag}->{$subfield}->{lib} =
653           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
654         $res->{$tag}->{$subfield}->{tab}              = $tab;
655         $res->{$tag}->{$subfield}->{mandatory}        = $mandatory;
656         $res->{$tag}->{$subfield}->{repeatable}       = $repeatable;
657         $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
658         $res->{$tag}->{$subfield}->{authtypecode}     = $authtypecode;
659         $res->{$tag}->{$subfield}->{value_builder}    = $value_builder;
660         $res->{$tag}->{$subfield}->{kohafield}        = $kohafield;
661         $res->{$tag}->{$subfield}->{seealso}          = $seealso;
662         $res->{$tag}->{$subfield}->{hidden}           = $hidden;
663         $res->{$tag}->{$subfield}->{isurl}            = $isurl;
664         $res->{$tag}->{$subfield}->{'link'}           = $link;
665         $res->{$tag}->{$subfield}->{defaultvalue}     = $defaultvalue;
666     }
667     return $res;
668 }
669
670 =head2 GetUsedMarcStructure
671
672     the same function as GetMarcStructure expcet it just take field
673     in tab 0-9. (used field)
674     
675     my $results = GetUsedMarcStructure($frameworkcode);
676     
677     L<$results> is a ref to an array which each case containts a ref
678     to a hash which each keys is the columns from marc_subfield_structure
679     
680     L<$frameworkcode> is the framework code. 
681     
682 =cut
683
684 sub GetUsedMarcStructure($){
685     my $frameworkcode = shift || '';
686     my $dbh           = C4::Context->dbh;
687     my $query         = qq/
688         SELECT *
689         FROM   marc_subfield_structure
690         WHERE   tab > -1 
691             AND frameworkcode = ?
692     /;
693     my @results;
694     my $sth = $dbh->prepare($query);
695     $sth->execute($frameworkcode);
696     while (my $row = $sth->fetchrow_hashref){
697         push @results,$row;
698     }
699     return \@results;
700 }
701
702 =head2 GetMarcFromKohaField
703
704 =over 4
705
706 ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
707 Returns the MARC fields & subfields mapped to the koha field 
708 for the given frameworkcode
709
710 =back
711
712 =cut
713
714 sub GetMarcFromKohaField {
715     my ( $kohafield, $frameworkcode ) = @_;
716     return 0, 0 unless $kohafield;
717     my $relations = C4::Context->marcfromkohafield;
718     return (
719         $relations->{$frameworkcode}->{$kohafield}->[0],
720         $relations->{$frameworkcode}->{$kohafield}->[1]
721     );
722 }
723
724 =head2 GetMarcBiblio
725
726 =over 4
727
728 Returns MARC::Record of the biblionumber passed in parameter.
729 the marc record contains both biblio & item datas
730
731 =back
732
733 =cut
734
735 sub GetMarcBiblio {
736     my $biblionumber = shift;
737     my $dbh          = C4::Context->dbh;
738     my $sth          =
739       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
740     $sth->execute($biblionumber);
741      my ($marcxml) = $sth->fetchrow;
742      MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
743      $marcxml =~ s/\x1e//g;
744      $marcxml =~ s/\x1f//g;
745      $marcxml =~ s/\x1d//g;
746      $marcxml =~ s/\x0f//g;
747      $marcxml =~ s/\x0c//g;  
748 #   warn $marcxml;
749     my $record = MARC::Record->new();
750     if ($marcxml) {
751         $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
752         if ($@) {warn $@;}
753 #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
754         return $record;
755     } else {
756         return undef;
757     }
758 }
759
760 =head2 GetXmlBiblio
761
762 =over 4
763
764 my $marcxml = GetXmlBiblio($biblionumber);
765
766 Returns biblioitems.marcxml of the biblionumber passed in parameter.
767 The XML contains both biblio & item datas
768
769 =back
770
771 =cut
772
773 sub GetXmlBiblio {
774     my ( $biblionumber ) = @_;
775     my $dbh = C4::Context->dbh;
776     my $sth =
777       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
778     $sth->execute($biblionumber);
779     my ($marcxml) = $sth->fetchrow;
780     return $marcxml;
781 }
782
783 =head2 GetAuthorisedValueDesc
784
785 =over 4
786
787 my $subfieldvalue =get_authorised_value_desc(
788     $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category);
789 Retrieve the complete description for a given authorised value.
790
791 Now takes $category and $value pair too.
792 my $auth_value_desc =GetAuthorisedValueDesc(
793     '','', 'DVD' ,'','','CCODE');
794
795 =back
796
797 =cut
798
799 sub GetAuthorisedValueDesc {
800     my ( $tag, $subfield, $value, $framework, $tagslib, $category ) = @_;
801     my $dbh = C4::Context->dbh;
802
803     if (!$category) {
804 #---- branch
805         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
806             return C4::Branch::GetBranchName($value);
807         }
808
809 #---- itemtypes
810         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
811             return getitemtypeinfo($value)->{description};
812         }
813
814 #---- "true" authorized value
815         $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'}
816     }
817
818     if ( $category ne "" ) {
819         my $sth =
820             $dbh->prepare(
821                     "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
822                     );
823         $sth->execute( $category, $value );
824         my $data = $sth->fetchrow_hashref;
825         return $data->{'lib'};
826     }
827     else {
828         return $value;    # if nothing is found return the original value
829     }
830 }
831
832 =head2 GetMarcNotes
833
834 =over 4
835
836 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
837 Get all notes from the MARC record and returns them in an array.
838 The note are stored in differents places depending on MARC flavour
839
840 =back
841
842 =cut
843
844 sub GetMarcNotes {
845     my ( $record, $marcflavour ) = @_;
846     my $scope;
847     if ( $marcflavour eq "MARC21" ) {
848         $scope = '5..';
849     }
850     else {    # assume unimarc if not marc21
851         $scope = '3..';
852     }
853     my @marcnotes;
854     my $note = "";
855     my $tag  = "";
856     my $marcnote;
857     foreach my $field ( $record->field($scope) ) {
858         my $value = $field->as_string();
859         if ( $note ne "" ) {
860             $marcnote = { marcnote => $note, };
861             push @marcnotes, $marcnote;
862             $note = $value;
863         }
864         if ( $note ne $value ) {
865             $note = $note . " " . $value;
866         }
867     }
868
869     if ( $note ) {
870         $marcnote = { marcnote => $note };
871         push @marcnotes, $marcnote;    #load last tag into array
872     }
873     return \@marcnotes;
874 }    # end GetMarcNotes
875
876 =head2 GetMarcSubjects
877
878 =over 4
879
880 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
881 Get all subjects from the MARC record and returns them in an array.
882 The subjects are stored in differents places depending on MARC flavour
883
884 =back
885
886 =cut
887
888 sub GetMarcSubjects {
889     my ( $record, $marcflavour ) = @_;
890     my ( $mintag, $maxtag );
891     if ( $marcflavour eq "MARC21" ) {
892         $mintag = "600";
893         $maxtag = "699";
894     }
895     else {    # assume unimarc if not marc21
896         $mintag = "600";
897         $maxtag = "611";
898     }
899     
900     my @marcsubjects;
901     my $subject = "";
902     my $subfield = "";
903     my $marcsubject;
904
905     foreach my $field ( $record->field('6..' )) {
906         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
907         my @subfields_loop;
908         my @subfields = $field->subfields();
909         my $counter = 0;
910         my @link_loop;
911         # if there is an authority link, build the link with an= subfield9
912         my $subfield9 = $field->subfield('9');
913         for my $subject_subfield (@subfields ) {
914             # don't load unimarc subfields 3,4,5
915             next if (($marcflavour eq "UNIMARC") and ($subject_subfield->[0] =~ (3|4|5) ) );
916             my $code = $subject_subfield->[0];
917             my $value = $subject_subfield->[1];
918             my $linkvalue = $value;
919             $linkvalue =~ s/(\(|\))//g;
920             my $operator = " and " unless $counter==0;
921             if ($subfield9) {
922                 @link_loop = ({'limit' => 'an' ,link => "$subfield9" });
923             } else {
924                 push @link_loop, {'limit' => 'su', link => $linkvalue, operator => $operator };
925             }
926             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
927             # ignore $9
928             my @this_link_loop = @link_loop;
929             push @subfields_loop, {code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($subject_subfield->[0] == 9 );
930             $counter++;
931         }
932                 
933         push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
934         
935     }
936         return \@marcsubjects;
937 }  #end getMARCsubjects
938
939 =head2 GetMarcAuthors
940
941 =over 4
942
943 authors = GetMarcAuthors($record,$marcflavour);
944 Get all authors from the MARC record and returns them in an array.
945 The authors are stored in differents places depending on MARC flavour
946
947 =back
948
949 =cut
950
951 sub GetMarcAuthors {
952     my ( $record, $marcflavour ) = @_;
953     my ( $mintag, $maxtag );
954     # tagslib useful for UNIMARC author reponsabilities
955     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.
956     if ( $marcflavour eq "MARC21" ) {
957         $mintag = "700";
958         $maxtag = "720"; 
959     }
960     elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
961         $mintag = "700";
962         $maxtag = "712";
963     }
964     else {
965         return;
966     }
967     my @marcauthors;
968
969     foreach my $field ( $record->fields ) {
970         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
971         my @subfields_loop;
972         my @link_loop;
973         my @subfields = $field->subfields();
974         my $count_auth = 0;
975         # if there is an authority link, build the link with Koha-Auth-Number: subfield9
976         my $subfield9 = $field->subfield('9');
977         for my $authors_subfield (@subfields) {
978             # don't load unimarc subfields 3, 5
979             next if ($marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~ (3|5) ) );
980             my $subfieldcode = $authors_subfield->[0];
981             my $value = $authors_subfield->[1];
982             my $linkvalue = $value;
983             $linkvalue =~ s/(\(|\))//g;
984             my $operator = " and " unless $count_auth==0;
985             # if we have an authority link, use that as the link, otherwise use standard searching
986             if ($subfield9) {
987                 @link_loop = ({'limit' => 'Koha-Auth-Number' ,link => "$subfield9" });
988             }
989             else {
990                 # reset $linkvalue if UNIMARC author responsibility
991                 if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq '4')) {
992                     $linkvalue = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
993                 }
994                 push @link_loop, {'limit' => 'au', link => $linkvalue, operator => $operator };
995             }
996             my @this_link_loop = @link_loop;
997             my $separator = C4::Context->preference("authoritysep") unless $count_auth==0;
998             push @subfields_loop, {code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($authors_subfield->[0] == 9 );
999             $count_auth++;
1000         }
1001         push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
1002     }
1003     return \@marcauthors;
1004 }
1005
1006 =head2 GetMarcUrls
1007
1008 =over 4
1009
1010 $marcurls = GetMarcUrls($record,$marcflavour);
1011 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1012 Assumes web resources (not uncommon in MARC21 to omit resource type ind) 
1013
1014 =back
1015
1016 =cut
1017
1018 sub GetMarcUrls {
1019     my ($record, $marcflavour) = @_;
1020     my @marcurls;
1021     my $marcurl;
1022     for my $field ($record->field('856')) {
1023         my $url = $field->subfield('u');
1024         my @notes;
1025         for my $note ( $field->subfield('z')) {
1026             push @notes , {note => $note};
1027         }        
1028         $marcurl = {  MARCURL => $url,
1029                       notes => \@notes,
1030                     };
1031         if($marcflavour eq 'MARC21') {
1032             my $s3 = $field->subfield('3');
1033             my $link = $field->subfield('y');
1034             $marcurl->{'linktext'} = $link || $s3 || $url ;;
1035             $marcurl->{'part'} = $s3 if($link);
1036             $marcurl->{'toc'} = 1 if($s3 =~ /^[Tt]able/) ;
1037         } else {
1038             $marcurl->{'linktext'} = $url;
1039         }
1040         push @marcurls, $marcurl;    
1041     }
1042     return \@marcurls;
1043 }  #end GetMarcUrls
1044
1045 =head2 GetMarcSeries
1046
1047 =over 4
1048
1049 $marcseriesarray = GetMarcSeries($record,$marcflavour);
1050 Get all series from the MARC record and returns them in an array.
1051 The series are stored in differents places depending on MARC flavour
1052
1053 =back
1054
1055 =cut
1056
1057 sub GetMarcSeries {
1058     my ($record, $marcflavour) = @_;
1059     my ($mintag, $maxtag);
1060     if ($marcflavour eq "MARC21") {
1061         $mintag = "440";
1062         $maxtag = "490";
1063     } else {           # assume unimarc if not marc21
1064         $mintag = "600";
1065         $maxtag = "619";
1066     }
1067
1068     my @marcseries;
1069     my $subjct = "";
1070     my $subfield = "";
1071     my $marcsubjct;
1072
1073     foreach my $field ($record->field('440'), $record->field('490')) {
1074         my @subfields_loop;
1075         #my $value = $field->subfield('a');
1076         #$marcsubjct = {MARCSUBJCT => $value,};
1077         my @subfields = $field->subfields();
1078         #warn "subfields:".join " ", @$subfields;
1079         my $counter = 0;
1080         my @link_loop;
1081         for my $series_subfield (@subfields) {
1082             my $volume_number;
1083             undef $volume_number;
1084             # see if this is an instance of a volume
1085             if ($series_subfield->[0] eq 'v') {
1086                 $volume_number=1;
1087             }
1088
1089             my $code = $series_subfield->[0];
1090             my $value = $series_subfield->[1];
1091             my $linkvalue = $value;
1092             $linkvalue =~ s/(\(|\))//g;
1093             my $operator = " and " unless $counter==0;
1094             push @link_loop, {link => $linkvalue, operator => $operator };
1095             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1096             if ($volume_number) {
1097             push @subfields_loop, {volumenum => $value};
1098             }
1099             else {
1100             push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number};
1101             }
1102             $counter++;
1103         }
1104         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1105         #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1106         #push @marcsubjcts, $marcsubjct;
1107         #$subjct = $value;
1108
1109     }
1110     my $marcseriessarray=\@marcseries;
1111     return $marcseriessarray;
1112 }  #end getMARCseriess
1113
1114 =head2 GetFrameworkCode
1115
1116 =over 4
1117
1118     $frameworkcode = GetFrameworkCode( $biblionumber )
1119
1120 =back
1121
1122 =cut
1123
1124 sub GetFrameworkCode {
1125     my ( $biblionumber ) = @_;
1126     my $dbh = C4::Context->dbh;
1127     my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
1128     $sth->execute($biblionumber);
1129     my ($frameworkcode) = $sth->fetchrow;
1130     return $frameworkcode;
1131 }
1132
1133 =head2 GetPublisherNameFromIsbn
1134
1135     $name = GetPublishercodeFromIsbn($isbn);
1136     if(defined $name){
1137         ...
1138     }
1139
1140 =cut
1141
1142 sub GetPublisherNameFromIsbn($){
1143     my $isbn = shift;
1144     $isbn =~ s/[- _]//g;
1145     $isbn =~ s/^0*//;
1146     my @codes = (split '-', DisplayISBN($isbn));
1147     my $code = $codes[0].$codes[1].$codes[2];
1148     my $dbh  = C4::Context->dbh;
1149     my $query = qq{
1150         SELECT distinct publishercode
1151         FROM   biblioitems
1152         WHERE  isbn LIKE ?
1153         AND    publishercode IS NOT NULL
1154         LIMIT 1
1155     };
1156     my $sth = $dbh->prepare($query);
1157     $sth->execute("$code%");
1158     my $name = $sth->fetchrow;
1159     return $name if length $name;
1160     return undef;
1161 }
1162
1163 =head2 TransformKohaToMarc
1164
1165 =over 4
1166
1167     $record = TransformKohaToMarc( $hash )
1168     This function builds partial MARC::Record from a hash
1169     Hash entries can be from biblio or biblioitems.
1170     This function is called in acquisition module, to create a basic catalogue entry from user entry
1171
1172 =back
1173
1174 =cut
1175
1176 sub TransformKohaToMarc {
1177
1178     my ( $hash ) = @_;
1179     my $dbh = C4::Context->dbh;
1180     my $sth =
1181     $dbh->prepare(
1182         "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
1183     );
1184     my $record = MARC::Record->new();
1185     foreach (keys %{$hash}) {
1186         &TransformKohaToMarcOneField( $sth, $record, $_,
1187             $hash->{$_}, '' );
1188         }
1189     return $record;
1190 }
1191
1192 =head2 TransformKohaToMarcOneField
1193
1194 =over 4
1195
1196     $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
1197
1198 =back
1199
1200 =cut
1201
1202 sub TransformKohaToMarcOneField {
1203     my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
1204     $frameworkcode='' unless $frameworkcode;
1205     my $tagfield;
1206     my $tagsubfield;
1207
1208     if ( !defined $sth ) {
1209         my $dbh = C4::Context->dbh;
1210         $sth = $dbh->prepare(
1211             "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
1212         );
1213     }
1214     $sth->execute( $frameworkcode, $kohafieldname );
1215     if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
1216         my $tag = $record->field($tagfield);
1217         if ($tag) {
1218             $tag->update( $tagsubfield => $value );
1219             $record->delete_field($tag);
1220             $record->insert_fields_ordered($tag);
1221         }
1222         else {
1223             $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
1224         }
1225     }
1226     return $record;
1227 }
1228
1229 =head2 TransformHtmlToXml
1230
1231 =over 4
1232
1233 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type )
1234
1235 $auth_type contains :
1236 - nothing : rebuild a biblio, un UNIMARC the encoding is in 100$a pos 26/27
1237 - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
1238 - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
1239
1240 =back
1241
1242 =cut
1243
1244 sub TransformHtmlToXml {
1245     my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
1246     my $xml = MARC::File::XML::header('UTF-8');
1247     $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
1248     MARC::File::XML->default_record_format($auth_type);
1249     # in UNIMARC, field 100 contains the encoding
1250     # check that there is one, otherwise the 
1251     # MARC::Record->new_from_xml will fail (and Koha will die)
1252     my $unimarc_and_100_exist=0;
1253     $unimarc_and_100_exist=1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
1254     my $prevvalue;
1255     my $prevtag = -1;
1256     my $first   = 1;
1257     my $j       = -1;
1258     for ( my $i = 0 ; $i <= @$tags ; $i++ ) {
1259         if (C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a") {
1260             # if we have a 100 field and it's values are not correct, skip them.
1261             # if we don't have any valid 100 field, we will create a default one at the end
1262             my $enc = substr( @$values[$i], 26, 2 );
1263             if ($enc eq '01' or $enc eq '50' or $enc eq '03') {
1264                 $unimarc_and_100_exist=1;
1265             } else {
1266                 next;
1267             }
1268         }
1269         @$values[$i] =~ s/&/&amp;/g;
1270         @$values[$i] =~ s/</&lt;/g;
1271         @$values[$i] =~ s/>/&gt;/g;
1272         @$values[$i] =~ s/"/&quot;/g;
1273         @$values[$i] =~ s/'/&apos;/g;
1274 #         if ( !utf8::is_utf8( @$values[$i] ) ) {
1275 #             utf8::decode( @$values[$i] );
1276 #         }
1277         if ( ( @$tags[$i] ne $prevtag ) ) {
1278             $j++ unless ( @$tags[$i] eq "" );
1279             if ( !$first ) {
1280                 $xml .= "</datafield>\n";
1281                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
1282                     && ( @$values[$i] ne "" ) )
1283                 {
1284                     my $ind1 = substr( @$indicator[$j], 0, 1 );
1285                     my $ind2;
1286                     if ( @$indicator[$j] ) {
1287                         $ind2 = substr( @$indicator[$j], 1, 1 );
1288                     }
1289                     else {
1290                         warn "Indicator in @$tags[$i] is empty";
1291                         $ind2 = " ";
1292                     }
1293                     $xml .=
1294 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1295                     $xml .=
1296 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1297                     $first = 0;
1298                 }
1299                 else {
1300                     $first = 1;
1301                 }
1302             }
1303             else {
1304                 if ( @$values[$i] ne "" ) {
1305
1306                     # leader
1307                     if ( @$tags[$i] eq "000" ) {
1308                         $xml .= "<leader>@$values[$i]</leader>\n";
1309                         $first = 1;
1310
1311                         # rest of the fixed fields
1312                     }
1313                     elsif ( @$tags[$i] < 10 ) {
1314                         $xml .=
1315 "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
1316                         $first = 1;
1317                     }
1318                     else {
1319                         my $ind1 = substr( @$indicator[$j], 0, 1 );
1320                         my $ind2 = substr( @$indicator[$j], 1, 1 );
1321                         $xml .=
1322 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1323                         $xml .=
1324 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1325                         $first = 0;
1326                     }
1327                 }
1328             }
1329         }
1330         else {    # @$tags[$i] eq $prevtag
1331             if ( @$values[$i] eq "" ) {
1332             }
1333             else {
1334                 if ($first) {
1335                     my $ind1 = substr( @$indicator[$j], 0, 1 );
1336                     my $ind2 = substr( @$indicator[$j], 1, 1 );
1337                     $xml .=
1338 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1339                     $first = 0;
1340                 }
1341                 $xml .=
1342 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1343             }
1344         }
1345         $prevtag = @$tags[$i];
1346     }
1347     if (C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist) {
1348 #     warn "SETTING 100 for $auth_type";
1349         use POSIX qw(strftime);
1350         my $string = strftime( "%Y%m%d", localtime(time) );
1351         # set 50 to position 26 is biblios, 13 if authorities
1352         my $pos=26;
1353         $pos=13 if $auth_type eq 'UNIMARCAUTH';
1354         $string = sprintf( "%-*s", 35, $string );
1355         substr( $string, $pos , 6, "50" );
1356         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
1357         $xml .= "<subfield code=\"a\">$string</subfield>\n";
1358         $xml .= "</datafield>\n";
1359     }
1360     $xml .= MARC::File::XML::footer();
1361     return $xml;
1362 }
1363
1364 =head2 TransformHtmlToMarc
1365
1366     L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
1367     L<$params> is a ref to an array as below:
1368     {
1369         'tag_010_indicator_531951' ,
1370         'tag_010_code_a_531951_145735' ,
1371         'tag_010_subfield_a_531951_145735' ,
1372         'tag_200_indicator_873510' ,
1373         'tag_200_code_a_873510_673465' ,
1374         'tag_200_subfield_a_873510_673465' ,
1375         'tag_200_code_b_873510_704318' ,
1376         'tag_200_subfield_b_873510_704318' ,
1377         'tag_200_code_e_873510_280822' ,
1378         'tag_200_subfield_e_873510_280822' ,
1379         'tag_200_code_f_873510_110730' ,
1380         'tag_200_subfield_f_873510_110730' ,
1381     }
1382     L<$cgi> is the CGI object which containts the value.
1383     L<$record> is the MARC::Record object.
1384
1385 =cut
1386
1387 sub TransformHtmlToMarc {
1388     my $params = shift;
1389     my $cgi    = shift;
1390     
1391     # creating a new record
1392     my $record  = MARC::Record->new();
1393     my $i=0;
1394     my @fields;
1395     while ($params->[$i]){ # browse all CGI params
1396         my $param = $params->[$i];
1397         my $newfield=0;
1398         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
1399         if ($param eq 'biblionumber') {
1400             my ( $biblionumbertagfield, $biblionumbertagsubfield ) =
1401                 &GetMarcFromKohaField( "biblio.biblionumber", '' );
1402             if ($biblionumbertagfield < 10) {
1403                 $newfield = MARC::Field->new(
1404                     $biblionumbertagfield,
1405                     $cgi->param($param),
1406                 );
1407             } else {
1408                 $newfield = MARC::Field->new(
1409                     $biblionumbertagfield,
1410                     '',
1411                     '',
1412                     "$biblionumbertagsubfield" => $cgi->param($param),
1413                 );
1414             }
1415             push @fields,$newfield if($newfield);
1416         } 
1417         elsif ($param =~ /^tag_(\d*)_indicator_/){ # new field start when having 'input name="..._indicator_..."
1418             my $tag  = $1;
1419             
1420             my $ind1 = substr($cgi->param($param),0,1);
1421             my $ind2 = substr($cgi->param($param),1,1);
1422             $newfield=0;
1423             my $j=$i+1;
1424             
1425             if($tag < 10){ # no code for theses fields
1426     # in MARC editor, 000 contains the leader.
1427                 if ($tag eq '000' ) {
1428                     $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==24;
1429     # between 001 and 009 (included)
1430                 } else {
1431                     $newfield = MARC::Field->new(
1432                         $tag,
1433                         $cgi->param($params->[$j+1]),
1434                     );
1435                 }
1436     # > 009, deal with subfields
1437             } else {
1438                 while($params->[$j] =~ /_code_/){ # browse all it's subfield
1439                     my $inner_param = $params->[$j];
1440                     if ($newfield){
1441                         if($cgi->param($params->[$j+1])){  # only if there is a value (code => value)
1442                             $newfield->add_subfields(
1443                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
1444                             );
1445                         }
1446                     } else {
1447                         if ( $cgi->param($params->[$j+1]) ) { # creating only if there is a value (code => value)
1448                             $newfield = MARC::Field->new(
1449                                 $tag,
1450                                 ''.$ind1,
1451                                 ''.$ind2,
1452                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1]),
1453                             );
1454                         }
1455                     }
1456                     $j+=2;
1457                 }
1458             }
1459             push @fields,$newfield if($newfield);
1460         }
1461         $i++;
1462     }
1463     
1464     $record->append_fields(@fields);
1465     return $record;
1466 }
1467
1468 # cache inverted MARC field map
1469 our $inverted_field_map;
1470
1471 =head2 TransformMarcToKoha
1472
1473 =over 4
1474
1475     $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
1476
1477 =back
1478
1479 Extract data from a MARC bib record into a hashref representing
1480 Koha biblio, biblioitems, and items fields. 
1481
1482 =cut
1483 sub TransformMarcToKoha {
1484     my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
1485
1486     my $result;
1487
1488     unless (defined $inverted_field_map) {
1489         $inverted_field_map = _get_inverted_marc_field_map();
1490     }
1491
1492     my %tables = ();
1493     if ($limit_table eq 'items') {
1494         $tables{'items'} = 1;
1495     } else {
1496         $tables{'items'} = 1;
1497         $tables{'biblio'} = 1;
1498         $tables{'biblioitems'} = 1;
1499     }
1500
1501     # traverse through record
1502     MARCFIELD: foreach my $field ($record->fields()) {
1503         my $tag = $field->tag();
1504         next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
1505         if ($field->is_control_field()) {
1506             my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
1507             ENTRY: foreach my $entry (@{ $kohafields }) {
1508                 my ($subfield, $table, $column) = @{ $entry };
1509                 next ENTRY unless exists $tables{$table};
1510                 my $key = _disambiguate($table, $column);
1511                 if ($result->{$key}) {
1512                     unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($field->data() eq "")) {
1513                         $result->{$key} .= " | " . $field->data();
1514                     }
1515                 } else {
1516                     $result->{$key} = $field->data();
1517                 }
1518             }
1519         } else {
1520             # deal with subfields
1521             MARCSUBFIELD: foreach my $sf ($field->subfields()) {
1522                 my $code = $sf->[0];
1523                 next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
1524                 my $value = $sf->[1];
1525                 SFENTRY: foreach my $entry (@{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} }) {
1526                     my ($table, $column) = @{ $entry };
1527                     next SFENTRY unless exists $tables{$table};
1528                     my $key = _disambiguate($table, $column);
1529                     if ($result->{$key}) {
1530                         unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
1531                             $result->{$key} .= " | " . $value;
1532                         }
1533                     } else {
1534                         $result->{$key} = $value;
1535                     }
1536                 }
1537             }
1538         }
1539     }
1540
1541     # modify copyrightdate to keep only the 1st year found
1542     if (exists $result->{'copyrightdate'}) {
1543         my $temp = $result->{'copyrightdate'};
1544         $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
1545         if ( $1 > 0 ) {
1546             $result->{'copyrightdate'} = $1;
1547         }
1548         else {                      # if no cYYYY, get the 1st date.
1549             $temp =~ m/(\d\d\d\d)/;
1550             $result->{'copyrightdate'} = $1;
1551         }
1552     }
1553
1554     # modify publicationyear to keep only the 1st year found
1555     if (exists $result->{'publicationyear'}) {
1556         my $temp = $result->{'publicationyear'};
1557         $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
1558         if ( $1 > 0 ) {
1559             $result->{'publicationyear'} = $1;
1560         }
1561         else {                      # if no cYYYY, get the 1st date.
1562             $temp =~ m/(\d\d\d\d)/;
1563             $result->{'publicationyear'} = $1;
1564         }
1565     }
1566
1567     return $result;
1568 }
1569
1570 sub _get_inverted_marc_field_map {
1571     my $field_map = {};
1572     my $relations = C4::Context->marcfromkohafield;
1573
1574     foreach my $frameworkcode (keys %{ $relations }) {
1575         foreach my $kohafield (keys %{ $relations->{$frameworkcode} }) {
1576             my $tag = $relations->{$frameworkcode}->{$kohafield}->[0];
1577             my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
1578             my ($table, $column) = split /[.]/, $kohafield, 2;
1579             push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
1580             push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
1581         }
1582     }
1583     return $field_map;
1584 }
1585
1586 =head2 _disambiguate
1587
1588 =over 4
1589
1590 $newkey = _disambiguate($table, $field);
1591
1592 This is a temporary hack to distinguish between the
1593 following sets of columns when using TransformMarcToKoha.
1594
1595 items.cn_source & biblioitems.cn_source
1596 items.cn_sort & biblioitems.cn_sort
1597
1598 Columns that are currently NOT distinguished (FIXME
1599 due to lack of time to fully test) are:
1600
1601 biblio.notes and biblioitems.notes
1602 biblionumber
1603 timestamp
1604 biblioitemnumber
1605
1606 FIXME - this is necessary because prefixing each column
1607 name with the table name would require changing lots
1608 of code and templates, and exposing more of the DB
1609 structure than is good to the UI templates, particularly
1610 since biblio and bibloitems may well merge in a future
1611 version.  In the future, it would also be good to 
1612 separate DB access and UI presentation field names
1613 more.
1614
1615 =back
1616
1617 =cut
1618
1619 sub _disambiguate {
1620     my ($table, $column) = @_;
1621     if ($column eq "cn_sort" or $column eq "cn_source") {
1622         return $table . '.' . $column;
1623     } else {
1624         return $column;
1625     }
1626
1627 }
1628
1629 =head2 get_koha_field_from_marc
1630
1631 =over 4
1632
1633 $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
1634
1635 Internal function to map data from the MARC record to a specific non-MARC field.
1636 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
1637
1638 =back
1639
1640 =cut
1641
1642 sub get_koha_field_from_marc {
1643     my ($koha_table,$koha_column,$record,$frameworkcode) = @_;
1644     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table.'.'.$koha_column, $frameworkcode );  
1645     my $kohafield;
1646     foreach my $field ( $record->field($tagfield) ) {
1647         if ( $field->tag() < 10 ) {
1648             if ( $kohafield ) {
1649                 $kohafield .= " | " . $field->data();
1650             }
1651             else {
1652                 $kohafield = $field->data();
1653             }
1654         }
1655         else {
1656             if ( $field->subfields ) {
1657                 my @subfields = $field->subfields();
1658                 foreach my $subfieldcount ( 0 .. $#subfields ) {
1659                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
1660                         if ( $kohafield ) {
1661                             $kohafield .=
1662                               " | " . $subfields[$subfieldcount][1];
1663                         }
1664                         else {
1665                             $kohafield =
1666                               $subfields[$subfieldcount][1];
1667                         }
1668                     }
1669                 }
1670             }
1671         }
1672     }
1673     return $kohafield;
1674
1675
1676
1677 =head2 TransformMarcToKohaOneField
1678
1679 =over 4
1680
1681 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
1682
1683 =back
1684
1685 =cut
1686
1687 sub TransformMarcToKohaOneField {
1688
1689     # FIXME ? if a field has a repeatable subfield that is used in old-db,
1690     # only the 1st will be retrieved...
1691     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
1692     my $res = "";
1693     my ( $tagfield, $subfield ) =
1694       GetMarcFromKohaField( $kohatable . "." . $kohafield,
1695         $frameworkcode );
1696     foreach my $field ( $record->field($tagfield) ) {
1697         if ( $field->tag() < 10 ) {
1698             if ( $result->{$kohafield} ) {
1699                 $result->{$kohafield} .= " | " . $field->data();
1700             }
1701             else {
1702                 $result->{$kohafield} = $field->data();
1703             }
1704         }
1705         else {
1706             if ( $field->subfields ) {
1707                 my @subfields = $field->subfields();
1708                 foreach my $subfieldcount ( 0 .. $#subfields ) {
1709                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
1710                         if ( $result->{$kohafield} ) {
1711                             $result->{$kohafield} .=
1712                               " | " . $subfields[$subfieldcount][1];
1713                         }
1714                         else {
1715                             $result->{$kohafield} =
1716                               $subfields[$subfieldcount][1];
1717                         }
1718                     }
1719                 }
1720             }
1721         }
1722     }
1723     return $result;
1724 }
1725
1726 =head1  OTHER FUNCTIONS
1727
1728 =head2 char_decode
1729
1730 =over 4
1731
1732 my $string = char_decode( $string, $encoding );
1733
1734 converts ISO 5426 coded string to UTF-8
1735 sloppy code : should be improved in next issue
1736
1737 =back
1738
1739 =cut
1740
1741 sub char_decode {
1742     my ( $string, $encoding ) = @_;
1743     $_ = $string;
1744
1745     $encoding = C4::Context->preference("marcflavour") unless $encoding;
1746     if ( $encoding eq "UNIMARC" ) {
1747
1748         #         s/\xe1/Æ/gm;
1749         s/\xe2/Ğ/gm;
1750         s/\xe9/Ø/gm;
1751         s/\xec/ş/gm;
1752         s/\xf1/æ/gm;
1753         s/\xf3/ğ/gm;
1754         s/\xf9/ø/gm;
1755         s/\xfb/ß/gm;
1756         s/\xc1\x61/à/gm;
1757         s/\xc1\x65/è/gm;
1758         s/\xc1\x69/ì/gm;
1759         s/\xc1\x6f/ò/gm;
1760         s/\xc1\x75/ù/gm;
1761         s/\xc1\x41/À/gm;
1762         s/\xc1\x45/È/gm;
1763         s/\xc1\x49/Ì/gm;
1764         s/\xc1\x4f/Ò/gm;
1765         s/\xc1\x55/Ù/gm;
1766         s/\xc2\x41/Á/gm;
1767         s/\xc2\x45/É/gm;
1768         s/\xc2\x49/Í/gm;
1769         s/\xc2\x4f/Ó/gm;
1770         s/\xc2\x55/Ú/gm;
1771         s/\xc2\x59/İ/gm;
1772         s/\xc2\x61/á/gm;
1773         s/\xc2\x65/é/gm;
1774         s/\xc2\x69/í/gm;
1775         s/\xc2\x6f/ó/gm;
1776         s/\xc2\x75/ú/gm;
1777         s/\xc2\x79/ı/gm;
1778         s/\xc3\x41/Â/gm;
1779         s/\xc3\x45/Ê/gm;
1780         s/\xc3\x49/Î/gm;
1781         s/\xc3\x4f/Ô/gm;
1782         s/\xc3\x55/Û/gm;
1783         s/\xc3\x61/â/gm;
1784         s/\xc3\x65/ê/gm;
1785         s/\xc3\x69/î/gm;
1786         s/\xc3\x6f/ô/gm;
1787         s/\xc3\x75/û/gm;
1788         s/\xc4\x41/Ã/gm;
1789         s/\xc4\x4e/Ñ/gm;
1790         s/\xc4\x4f/Õ/gm;
1791         s/\xc4\x61/ã/gm;
1792         s/\xc4\x6e/ñ/gm;
1793         s/\xc4\x6f/õ/gm;
1794         s/\xc8\x41/Ä/gm;
1795         s/\xc8\x45/Ë/gm;
1796         s/\xc8\x49/Ï/gm;
1797         s/\xc8\x61/ä/gm;
1798         s/\xc8\x65/ë/gm;
1799         s/\xc8\x69/ï/gm;
1800         s/\xc8\x6F/ö/gm;
1801         s/\xc8\x75/ü/gm;
1802         s/\xc8\x76/ÿ/gm;
1803         s/\xc9\x41/Ä/gm;
1804         s/\xc9\x45/Ë/gm;
1805         s/\xc9\x49/Ï/gm;
1806         s/\xc9\x4f/Ö/gm;
1807         s/\xc9\x55/Ü/gm;
1808         s/\xc9\x61/ä/gm;
1809         s/\xc9\x6f/ö/gm;
1810         s/\xc9\x75/ü/gm;
1811         s/\xca\x41/Å/gm;
1812         s/\xca\x61/å/gm;
1813         s/\xd0\x43/Ç/gm;
1814         s/\xd0\x63/ç/gm;
1815
1816         # this handles non-sorting blocks (if implementation requires this)
1817         $string = nsb_clean($_);
1818     }
1819     elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
1820         ##MARC-8 to UTF-8
1821
1822         s/\xe1\x61/à/gm;
1823         s/\xe1\x65/è/gm;
1824         s/\xe1\x69/ì/gm;
1825         s/\xe1\x6f/ò/gm;
1826         s/\xe1\x75/ù/gm;
1827         s/\xe1\x41/À/gm;
1828         s/\xe1\x45/È/gm;
1829         s/\xe1\x49/Ì/gm;
1830         s/\xe1\x4f/Ò/gm;
1831         s/\xe1\x55/Ù/gm;
1832         s/\xe2\x41/Á/gm;
1833         s/\xe2\x45/É/gm;
1834         s/\xe2\x49/Í/gm;
1835         s/\xe2\x4f/Ó/gm;
1836         s/\xe2\x55/Ú/gm;
1837         s/\xe2\x59/İ/gm;
1838         s/\xe2\x61/á/gm;
1839         s/\xe2\x65/é/gm;
1840         s/\xe2\x69/í/gm;
1841         s/\xe2\x6f/ó/gm;
1842         s/\xe2\x75/ú/gm;
1843         s/\xe2\x79/ı/gm;
1844         s/\xe3\x41/Â/gm;
1845         s/\xe3\x45/Ê/gm;
1846         s/\xe3\x49/Î/gm;
1847         s/\xe3\x4f/Ô/gm;
1848         s/\xe3\x55/Û/gm;
1849         s/\xe3\x61/â/gm;
1850         s/\xe3\x65/ê/gm;
1851         s/\xe3\x69/î/gm;
1852         s/\xe3\x6f/ô/gm;
1853         s/\xe3\x75/û/gm;
1854         s/\xe4\x41/Ã/gm;
1855         s/\xe4\x4e/Ñ/gm;
1856         s/\xe4\x4f/Õ/gm;
1857         s/\xe4\x61/ã/gm;
1858         s/\xe4\x6e/ñ/gm;
1859         s/\xe4\x6f/õ/gm;
1860         s/\xe6\x41/Ă/gm;
1861         s/\xe6\x45/Ĕ/gm;
1862         s/\xe6\x65/ĕ/gm;
1863         s/\xe6\x61/ă/gm;
1864         s/\xe8\x45/Ë/gm;
1865         s/\xe8\x49/Ï/gm;
1866         s/\xe8\x65/ë/gm;
1867         s/\xe8\x69/ï/gm;
1868         s/\xe8\x76/ÿ/gm;
1869         s/\xe9\x41/A/gm;
1870         s/\xe9\x4f/O/gm;
1871         s/\xe9\x55/U/gm;
1872         s/\xe9\x61/a/gm;
1873         s/\xe9\x6f/o/gm;
1874         s/\xe9\x75/u/gm;
1875         s/\xea\x41/A/gm;
1876         s/\xea\x61/a/gm;
1877
1878         #Additional Turkish characters
1879         s/\x1b//gm;
1880         s/\x1e//gm;
1881         s/(\xf0)s/\xc5\x9f/gm;
1882         s/(\xf0)S/\xc5\x9e/gm;
1883         s/(\xf0)c/ç/gm;
1884         s/(\xf0)C/Ç/gm;
1885         s/\xe7\x49/\\xc4\xb0/gm;
1886         s/(\xe6)G/\xc4\x9e/gm;
1887         s/(\xe6)g/ğ\xc4\x9f/gm;
1888         s/\xB8/ı/gm;
1889         s/\xB9/£/gm;
1890         s/(\xe8|\xc8)o/ö/gm;
1891         s/(\xe8|\xc8)O/Ö/gm;
1892         s/(\xe8|\xc8)u/ü/gm;
1893         s/(\xe8|\xc8)U/Ü/gm;
1894         s/\xc2\xb8/\xc4\xb1/gm;
1895         s/¸/\xc4\xb1/gm;
1896
1897         # this handles non-sorting blocks (if implementation requires this)
1898         $string = nsb_clean($_);
1899     }
1900     return ($string);
1901 }
1902
1903 =head2 nsb_clean
1904
1905 =over 4
1906
1907 my $string = nsb_clean( $string, $encoding );
1908
1909 =back
1910
1911 =cut
1912
1913 sub nsb_clean {
1914     my $NSB      = '\x88';    # NSB : begin Non Sorting Block
1915     my $NSE      = '\x89';    # NSE : Non Sorting Block end
1916                               # handles non sorting blocks
1917     my ($string) = @_;
1918     $_ = $string;
1919     s/$NSB/(/gm;
1920     s/[ ]{0,1}$NSE/) /gm;
1921     $string = $_;
1922     return ($string);
1923 }
1924
1925 =head2 PrepareItemrecordDisplay
1926
1927 =over 4
1928
1929 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber);
1930
1931 Returns a hash with all the fields for Display a given item data in a template
1932
1933 =back
1934
1935 =cut
1936
1937 sub PrepareItemrecordDisplay {
1938
1939     my ( $bibnum, $itemnum ) = @_;
1940
1941     my $dbh = C4::Context->dbh;
1942     my $frameworkcode = &GetFrameworkCode( $bibnum );
1943     my ( $itemtagfield, $itemtagsubfield ) =
1944       &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
1945     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
1946     my $itemrecord = GetMarcItem( $bibnum, $itemnum) if ($itemnum);
1947     my @loop_data;
1948     my $authorised_values_sth =
1949       $dbh->prepare(
1950 "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"
1951       );
1952     foreach my $tag ( sort keys %{$tagslib} ) {
1953         my $previous_tag = '';
1954         if ( $tag ne '' ) {
1955             # loop through each subfield
1956             my $cntsubf;
1957             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
1958                 next if ( subfield_is_koha_internal_p($subfield) );
1959                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
1960                 my %subfield_data;
1961                 $subfield_data{tag}           = $tag;
1962                 $subfield_data{subfield}      = $subfield;
1963                 $subfield_data{countsubfield} = $cntsubf++;
1964                 $subfield_data{kohafield}     =
1965                   $tagslib->{$tag}->{$subfield}->{'kohafield'};
1966
1967          #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
1968                 $subfield_data{marc_lib} =
1969                     "<span id=\"error\" title=\""
1970                   . $tagslib->{$tag}->{$subfield}->{lib} . "\">"
1971                   . substr( $tagslib->{$tag}->{$subfield}->{lib}, 0, 12 )
1972                   . "</span>";
1973                 $subfield_data{mandatory} =
1974                   $tagslib->{$tag}->{$subfield}->{mandatory};
1975                 $subfield_data{repeatable} =
1976                   $tagslib->{$tag}->{$subfield}->{repeatable};
1977                 $subfield_data{hidden} = "display:none"
1978                   if $tagslib->{$tag}->{$subfield}->{hidden};
1979                 my ( $x, $value );
1980                 ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord )
1981                   if ($itemrecord);
1982                 $value =~ s/"/&quot;/g;
1983
1984                 # search for itemcallnumber if applicable
1985                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
1986                     'items.itemcallnumber'
1987                     && C4::Context->preference('itemcallnumber') )
1988                 {
1989                     my $CNtag =
1990                       substr( C4::Context->preference('itemcallnumber'), 0, 3 );
1991                     my $CNsubfield =
1992                       substr( C4::Context->preference('itemcallnumber'), 3, 1 );
1993                     my $temp = $itemrecord->field($CNtag) if ($itemrecord);
1994                     if ($temp) {
1995                         $value = $temp->subfield($CNsubfield);
1996                     }
1997                 }
1998                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
1999                     my @authorised_values;
2000                     my %authorised_lib;
2001
2002                     # builds list, depending on authorised value...
2003                     #---- branch
2004                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
2005                         "branches" )
2006                     {
2007                         if ( ( C4::Context->preference("IndependantBranches") )
2008                             && ( C4::Context->userenv->{flags} != 1 ) )
2009                         {
2010                             my $sth =
2011                               $dbh->prepare(
2012                                 "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname"
2013                               );
2014                             $sth->execute( C4::Context->userenv->{branch} );
2015                             push @authorised_values, ""
2016                               unless (
2017                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2018                             while ( my ( $branchcode, $branchname ) =
2019                                 $sth->fetchrow_array )
2020                             {
2021                                 push @authorised_values, $branchcode;
2022                                 $authorised_lib{$branchcode} = $branchname;
2023                             }
2024                         }
2025                         else {
2026                             my $sth =
2027                               $dbh->prepare(
2028                                 "SELECT branchcode,branchname FROM branches ORDER BY branchname"
2029                               );
2030                             $sth->execute;
2031                             push @authorised_values, ""
2032                               unless (
2033                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2034                             while ( my ( $branchcode, $branchname ) =
2035                                 $sth->fetchrow_array )
2036                             {
2037                                 push @authorised_values, $branchcode;
2038                                 $authorised_lib{$branchcode} = $branchname;
2039                             }
2040                         }
2041
2042                         #----- itemtypes
2043                     }
2044                     elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
2045                         "itemtypes" )
2046                     {
2047                         my $sth =
2048                           $dbh->prepare(
2049                             "SELECT itemtype,description FROM itemtypes ORDER BY description"
2050                           );
2051                         $sth->execute;
2052                         push @authorised_values, ""
2053                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2054                         while ( my ( $itemtype, $description ) =
2055                             $sth->fetchrow_array )
2056                         {
2057                             push @authorised_values, $itemtype;
2058                             $authorised_lib{$itemtype} = $description;
2059                         }
2060
2061                         #---- "true" authorised value
2062                     }
2063                     else {
2064                         $authorised_values_sth->execute(
2065                             $tagslib->{$tag}->{$subfield}->{authorised_value} );
2066                         push @authorised_values, ""
2067                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2068                         while ( my ( $value, $lib ) =
2069                             $authorised_values_sth->fetchrow_array )
2070                         {
2071                             push @authorised_values, $value;
2072                             $authorised_lib{$value} = $lib;
2073                         }
2074                     }
2075                     $subfield_data{marc_value} = CGI::scrolling_list(
2076                         -name     => 'field_value',
2077                         -values   => \@authorised_values,
2078                         -default  => "$value",
2079                         -labels   => \%authorised_lib,
2080                         -size     => 1,
2081                         -tabindex => '',
2082                         -multiple => 0,
2083                     );
2084                 }
2085                 elsif ( $tagslib->{$tag}->{$subfield}->{thesaurus_category} ) {
2086                     $subfield_data{marc_value} =
2087 "<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>";
2088
2089 #"
2090 # COMMENTED OUT because No $i is provided with this API.
2091 # And thus, no value_builder can be activated.
2092 # BUT could be thought over.
2093 #         } elsif ($tagslib->{$tag}->{$subfield}->{'value_builder'}) {
2094 #             my $plugin="value_builder/".$tagslib->{$tag}->{$subfield}->{'value_builder'};
2095 #             require $plugin;
2096 #             my $extended_param = plugin_parameters($dbh,$itemrecord,$tagslib,$i,0);
2097 #             my ($function_name,$javascript) = plugin_javascript($dbh,$record,$tagslib,$i,0);
2098 #             $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";
2099                 }
2100                 else {
2101                     $subfield_data{marc_value} =
2102 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=50 maxlength=255>";
2103                 }
2104                 push( @loop_data, \%subfield_data );
2105             }
2106         }
2107     }
2108     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2109       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2110     return {
2111         'itemtagfield'    => $itemtagfield,
2112         'itemtagsubfield' => $itemtagsubfield,
2113         'itemnumber'      => $itemnumber,
2114         'iteminformation' => \@loop_data
2115     };
2116 }
2117 #"
2118
2119 #
2120 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2121 # at the same time
2122 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2123 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2124 # =head2 ModZebrafiles
2125
2126 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2127
2128 # =cut
2129
2130 # sub ModZebrafiles {
2131
2132 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2133
2134 #     my $op;
2135 #     my $zebradir =
2136 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2137 #     unless ( opendir( DIR, "$zebradir" ) ) {
2138 #         warn "$zebradir not found";
2139 #         return;
2140 #     }
2141 #     closedir DIR;
2142 #     my $filename = $zebradir . $biblionumber;
2143
2144 #     if ($record) {
2145 #         open( OUTPUT, ">", $filename . ".xml" );
2146 #         print OUTPUT $record;
2147 #         close OUTPUT;
2148 #     }
2149 # }
2150
2151 =head2 ModZebra
2152
2153 =over 4
2154
2155 ModZebra( $biblionumber, $op, $server, $newRecord );
2156
2157     $biblionumber is the biblionumber we want to index
2158     $op is specialUpdate or delete, and is used to know what we want to do
2159     $server is the server that we want to update
2160     $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.
2161     
2162 =back
2163
2164 =cut
2165
2166 sub ModZebra {
2167 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2168     my ( $biblionumber, $op, $server, $newRecord ) = @_;
2169     my $dbh=C4::Context->dbh;
2170
2171     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2172     # at the same time
2173     # replaced by a zebraqueue table, that is filled with ModZebra to run.
2174     # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2175
2176     if (C4::Context->preference("NoZebra")) {
2177         # lock the nozebra table : we will read index lines, update them in Perl process
2178         # and write everything in 1 transaction.
2179         # lock the table to avoid someone else overwriting what we are doing
2180         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE');
2181         my %result; # the result hash that will be builded by deletion / add, and written on mySQL at the end, to improve speed
2182         my $record;
2183         if ($server eq 'biblioserver') {
2184             $record= GetMarcBiblio($biblionumber);
2185         } else {
2186             $record= C4::AuthoritiesMarc::GetAuthority($biblionumber);
2187         }
2188         if ($op eq 'specialUpdate') {
2189             # OK, we have to add or update the record
2190             # 1st delete (virtually, in indexes), if record actually exists
2191             if ($record) { 
2192                 %result = _DelBiblioNoZebra($biblionumber,$record,$server);
2193             }
2194             # ... add the record
2195             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
2196         } else {
2197             # it's a deletion, delete the record...
2198             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2199             %result=_DelBiblioNoZebra($biblionumber,$record,$server);
2200         }
2201         # ok, now update the database...
2202         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2203         foreach my $key (keys %result) {
2204             foreach my $index (keys %{$result{$key}}) {
2205                 $sth->execute($result{$key}->{$index}, $server, $key, $index);
2206             }
2207         }
2208         $dbh->do('UNLOCK TABLES');
2209
2210     } else {
2211         #
2212         # we use zebra, just fill zebraqueue table
2213         #
2214         my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2215         $sth->execute($biblionumber,$server,$op);
2216         $sth->finish;
2217     }
2218 }
2219
2220 =head2 GetNoZebraIndexes
2221
2222     %indexes = GetNoZebraIndexes;
2223     
2224     return the data from NoZebraIndexes syspref.
2225
2226 =cut
2227
2228 sub GetNoZebraIndexes {
2229     my $index = C4::Context->preference('NoZebraIndexes');
2230     my %indexes;
2231     foreach my $line (split /('|"),/,$index) {
2232         $line =~ /(.*)=>(.*)/;
2233         my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
2234         my $fields = $2;
2235         $index =~ s/'|"|\s//g;
2236
2237
2238         $fields =~ s/'|"|\s//g;
2239         $indexes{$index}=$fields;
2240     }
2241     return %indexes;
2242 }
2243
2244 =head1 INTERNAL FUNCTIONS
2245
2246 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2247
2248     function to delete a biblio in NoZebra indexes
2249     This function does NOT delete anything in database : it reads all the indexes entries
2250     that have to be deleted & delete them in the hash
2251     The SQL part is done either :
2252     - after the Add if we are modifying a biblio (delete + add again)
2253     - immediatly after this sub if we are doing a true deletion.
2254     $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2255
2256 =cut
2257
2258
2259 sub _DelBiblioNoZebra {
2260     my ($biblionumber, $record, $server)=@_;
2261     
2262     # Get the indexes
2263     my $dbh = C4::Context->dbh;
2264     # Get the indexes
2265     my %index;
2266     my $title;
2267     if ($server eq 'biblioserver') {
2268         %index=GetNoZebraIndexes;
2269         # get title of the record (to store the 10 first letters with the index)
2270         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
2271         $title = lc($record->subfield($titletag,$titlesubfield));
2272     } else {
2273         # for authorities, the "title" is the $a mainentry
2274         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
2275         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2276         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2277         $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
2278         $index{'mainentry'}    = $authref->{'auth_tag_to_report'}.'*';
2279         $index{'auth_type'}    = '152b';
2280     }
2281     
2282     my %result;
2283     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2284     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2285     # limit to 10 char, should be enough, and limit the DB size
2286     $title = substr($title,0,10);
2287     #parse each field
2288     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2289     foreach my $field ($record->fields()) {
2290         #parse each subfield
2291         next if $field->tag <10;
2292         foreach my $subfield ($field->subfields()) {
2293             my $tag = $field->tag();
2294             my $subfieldcode = $subfield->[0];
2295             my $indexed=0;
2296             # check each index to see if the subfield is stored somewhere
2297             # otherwise, store it in __RAW__ index
2298             foreach my $key (keys %index) {
2299 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2300                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2301                     $indexed=1;
2302                     my $line= lc $subfield->[1];
2303                     # remove meaningless value in the field...
2304                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2305                     # ... and split in words
2306                     foreach (split / /,$line) {
2307                         next unless $_; # skip  empty values (multiple spaces)
2308                         # if the entry is already here, do nothing, the biblionumber has already be removed
2309                         unless ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2310                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2311                             $sth2->execute($server,$key,$_);
2312                             my $existing_biblionumbers = $sth2->fetchrow;
2313                             # it exists
2314                             if ($existing_biblionumbers) {
2315 #                                 warn " existing for $key $_: $existing_biblionumbers";
2316                                 $result{$key}->{$_} =$existing_biblionumbers;
2317                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2318                             }
2319                         }
2320                     }
2321                 }
2322             }
2323             # the subfield is not indexed, store it in __RAW__ index anyway
2324             unless ($indexed) {
2325                 my $line= lc $subfield->[1];
2326                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2327                 # ... and split in words
2328                 foreach (split / /,$line) {
2329                     next unless $_; # skip  empty values (multiple spaces)
2330                     # if the entry is already here, do nothing, the biblionumber has already be removed
2331                     unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2332                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2333                         $sth2->execute($server,'__RAW__',$_);
2334                         my $existing_biblionumbers = $sth2->fetchrow;
2335                         # it exists
2336                         if ($existing_biblionumbers) {
2337                             $result{'__RAW__'}->{$_} =$existing_biblionumbers;
2338                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2339                         }
2340                     }
2341                 }
2342             }
2343         }
2344     }
2345     return %result;
2346 }
2347
2348 =head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
2349
2350     function to add a biblio in NoZebra indexes
2351
2352 =cut
2353
2354 sub _AddBiblioNoZebra {
2355     my ($biblionumber, $record, $server, %result)=@_;
2356     my $dbh = C4::Context->dbh;
2357     # Get the indexes
2358     my %index;
2359     my $title;
2360     if ($server eq 'biblioserver') {
2361         %index=GetNoZebraIndexes;
2362         # get title of the record (to store the 10 first letters with the index)
2363         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
2364         $title = lc($record->subfield($titletag,$titlesubfield));
2365     } else {
2366         # warn "server : $server";
2367         # for authorities, the "title" is the $a mainentry
2368         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
2369         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2370         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2371         $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
2372         $index{'mainentry'}     = $authref->{auth_tag_to_report}.'*';
2373         $index{'auth_type'}     = '152b';
2374     }
2375
2376     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2377     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2378     # limit to 10 char, should be enough, and limit the DB size
2379     $title = substr($title,0,10);
2380     #parse each field
2381     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2382     foreach my $field ($record->fields()) {
2383         #parse each subfield
2384         next if $field->tag <10;
2385         foreach my $subfield ($field->subfields()) {
2386             my $tag = $field->tag();
2387             my $subfieldcode = $subfield->[0];
2388             my $indexed=0;
2389             # check each index to see if the subfield is stored somewhere
2390             # otherwise, store it in __RAW__ index
2391             foreach my $key (keys %index) {
2392 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2393                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2394                     $indexed=1;
2395                     my $line= lc $subfield->[1];
2396                     # remove meaningless value in the field...
2397                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2398                     # ... and split in words
2399                     foreach (split / /,$line) {
2400                         next unless $_; # skip  empty values (multiple spaces)
2401                         # if the entry is already here, improve weight
2402 #                         warn "managing $_";
2403                         if ($result{$key}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
2404                             my $weight=$1+1;
2405                             $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
2406                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2407                         } else {
2408                             # get the value if it exist in the nozebra table, otherwise, create it
2409                             $sth2->execute($server,$key,$_);
2410                             my $existing_biblionumbers = $sth2->fetchrow;
2411                             # it exists
2412                             if ($existing_biblionumbers) {
2413                                 $result{$key}->{"$_"} =$existing_biblionumbers;
2414                                 my $weight=$1+1;
2415                                 $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
2416                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2417                             # create a new ligne for this entry
2418                             } else {
2419 #                             warn "INSERT : $server / $key / $_";
2420                                 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
2421                                 $result{$key}->{"$_"}.="$biblionumber,$title-1;";
2422                             }
2423                         }
2424                     }
2425                 }
2426             }
2427             # the subfield is not indexed, store it in __RAW__ index anyway
2428             unless ($indexed) {
2429                 my $line= lc $subfield->[1];
2430                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2431                 # ... and split in words
2432                 foreach (split / /,$line) {
2433                     next unless $_; # skip  empty values (multiple spaces)
2434                     # if the entry is already here, improve weight
2435                     if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
2436                         my $weight=$1+1;
2437                         $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
2438                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2439                     } else {
2440                         # get the value if it exist in the nozebra table, otherwise, create it
2441                         $sth2->execute($server,'__RAW__',$_);
2442                         my $existing_biblionumbers = $sth2->fetchrow;
2443                         # it exists
2444                         if ($existing_biblionumbers) {
2445                             $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
2446                             my $weight=$1+1;
2447                             $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
2448                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2449                         # create a new ligne for this entry
2450                         } else {
2451                             $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).',  indexname="__RAW__",value='.$dbh->quote($_));
2452                             $result{'__RAW__'}->{"$_"}.="$biblionumber,$title-1;";
2453                         }
2454                     }
2455                 }
2456             }
2457         }
2458     }
2459     return %result;
2460 }
2461
2462
2463 =head2 _find_value
2464
2465 =over 4
2466
2467 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2468
2469 Find the given $subfield in the given $tag in the given
2470 MARC::Record $record.  If the subfield is found, returns
2471 the (indicators, value) pair; otherwise, (undef, undef) is
2472 returned.
2473
2474 PROPOSITION :
2475 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2476 I suggest we export it from this module.
2477
2478 =back
2479
2480 =cut
2481
2482 sub _find_value {
2483     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2484     my @result;
2485     my $indicator;
2486     if ( $tagfield < 10 ) {
2487         if ( $record->field($tagfield) ) {
2488             push @result, $record->field($tagfield)->data();
2489         }
2490         else {
2491             push @result, "";
2492         }
2493     }
2494     else {
2495         foreach my $field ( $record->field($tagfield) ) {
2496             my @subfields = $field->subfields();
2497             foreach my $subfield (@subfields) {
2498                 if ( @$subfield[0] eq $insubfield ) {
2499                     push @result, @$subfield[1];
2500                     $indicator = $field->indicator(1) . $field->indicator(2);
2501                 }
2502             }
2503         }
2504     }
2505     return ( $indicator, @result );
2506 }
2507
2508 =head2 _koha_marc_update_bib_ids
2509
2510 =over 4
2511
2512 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
2513
2514 Internal function to add or update biblionumber and biblioitemnumber to
2515 the MARC XML.
2516
2517 =back
2518
2519 =cut
2520
2521 sub _koha_marc_update_bib_ids {
2522     my ($record, $frameworkcode, $biblionumber, $biblioitemnumber) = @_;
2523
2524     # we must add bibnum and bibitemnum in MARC::Record...
2525     # we build the new field with biblionumber and biblioitemnumber
2526     # we drop the original field
2527     # we add the new builded field.
2528     my ($biblio_tag, $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
2529     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
2530
2531     if ($biblio_tag != $biblioitem_tag) {
2532         # biblionumber & biblioitemnumber are in different fields
2533
2534         # deal with biblionumber
2535         my ($new_field, $old_field);
2536         if ($biblio_tag < 10) {
2537             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
2538         } else {
2539             $new_field =
2540               MARC::Field->new( $biblio_tag, '', '',
2541                 "$biblio_subfield" => $biblionumber );
2542         }
2543
2544         # drop old field and create new one...
2545         $old_field = $record->field($biblio_tag);
2546         $record->delete_field($old_field);
2547         $record->append_fields($new_field);
2548
2549         # deal with biblioitemnumber
2550         if ($biblioitem_tag < 10) {
2551             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
2552         } else {
2553             $new_field =
2554               MARC::Field->new( $biblioitem_tag, '', '',
2555                 "$biblioitem_subfield" => $biblioitemnumber, );
2556         }
2557         # drop old field and create new one...
2558         $old_field = $record->field($biblioitem_tag);
2559         $record->delete_field($old_field);
2560         $record->insert_fields_ordered($new_field);
2561
2562     } else {
2563         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
2564         my $new_field = MARC::Field->new(
2565             $biblio_tag, '', '',
2566             "$biblio_subfield" => $biblionumber,
2567             "$biblioitem_subfield" => $biblioitemnumber
2568         );
2569
2570         # drop old field and create new one...
2571         my $old_field = $record->field($biblio_tag);
2572         $record->delete_field($old_field);
2573         $record->insert_fields_ordered($new_field);
2574     }
2575 }
2576
2577 =head2 _koha_add_biblio
2578
2579 =over 4
2580
2581 my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
2582
2583 Internal function to add a biblio ($biblio is a hash with the values)
2584
2585 =back
2586
2587 =cut
2588
2589 sub _koha_add_biblio {
2590     my ( $dbh, $biblio, $frameworkcode ) = @_;
2591
2592     my $error;
2593
2594     # set the series flag
2595     my $serial = 0;
2596     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
2597
2598     my $query = 
2599         "INSERT INTO biblio
2600         SET frameworkcode = ?,
2601             author = ?,
2602             title = ?,
2603             unititle =?,
2604             notes = ?,
2605             serial = ?,
2606             seriestitle = ?,
2607             copyrightdate = ?,
2608             datecreated=NOW(),
2609             abstract = ?
2610         ";
2611     my $sth = $dbh->prepare($query);
2612     $sth->execute(
2613         $frameworkcode,
2614         $biblio->{'author'},
2615         $biblio->{'title'},
2616         $biblio->{'unititle'},
2617         $biblio->{'notes'},
2618         $serial,
2619         $biblio->{'seriestitle'},
2620         $biblio->{'copyrightdate'},
2621         $biblio->{'abstract'}
2622     );
2623
2624     my $biblionumber = $dbh->{'mysql_insertid'};
2625     if ( $dbh->errstr ) {
2626         $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
2627         warn $error;
2628     }
2629
2630     $sth->finish();
2631     #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
2632     return ($biblionumber,$error);
2633 }
2634
2635 =head2 _koha_modify_biblio
2636
2637 =over 4
2638
2639 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
2640
2641 Internal function for updating the biblio table
2642
2643 =back
2644
2645 =cut
2646
2647 sub _koha_modify_biblio {
2648     my ( $dbh, $biblio, $frameworkcode ) = @_;
2649     my $error;
2650
2651     my $query = "
2652         UPDATE biblio
2653         SET    frameworkcode = ?,
2654                author = ?,
2655                title = ?,
2656                unititle = ?,
2657                notes = ?,
2658                serial = ?,
2659                seriestitle = ?,
2660                copyrightdate = ?,
2661                abstract = ?
2662         WHERE  biblionumber = ?
2663         "
2664     ;
2665     my $sth = $dbh->prepare($query);
2666     
2667     $sth->execute(
2668         $frameworkcode,
2669         $biblio->{'author'},
2670         $biblio->{'title'},
2671         $biblio->{'unititle'},
2672         $biblio->{'notes'},
2673         $biblio->{'serial'},
2674         $biblio->{'seriestitle'},
2675         $biblio->{'copyrightdate'},
2676         $biblio->{'abstract'},
2677         $biblio->{'biblionumber'}
2678     ) if $biblio->{'biblionumber'};
2679
2680     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
2681         $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
2682         warn $error;
2683     }
2684     return ( $biblio->{'biblionumber'},$error );
2685 }
2686
2687 =head2 _koha_modify_biblioitem_nonmarc
2688
2689 =over 4
2690
2691 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
2692
2693 Updates biblioitems row except for marc and marcxml, which should be changed
2694 via ModBiblioMarc
2695
2696 =back
2697
2698 =cut
2699
2700 sub _koha_modify_biblioitem_nonmarc {
2701     my ( $dbh, $biblioitem ) = @_;
2702     my $error;
2703
2704     # re-calculate the cn_sort, it may have changed
2705     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2706
2707     my $query = 
2708     "UPDATE biblioitems 
2709     SET biblionumber    = ?,
2710         volume          = ?,
2711         number          = ?,
2712         itemtype        = ?,
2713         isbn            = ?,
2714         issn            = ?,
2715         publicationyear = ?,
2716         publishercode   = ?,
2717         volumedate      = ?,
2718         volumedesc      = ?,
2719         collectiontitle = ?,
2720         collectionissn  = ?,
2721         collectionvolume= ?,
2722         editionstatement= ?,
2723         editionresponsibility = ?,
2724         illus           = ?,
2725         pages           = ?,
2726         notes           = ?,
2727         size            = ?,
2728         place           = ?,
2729         lccn            = ?,
2730         url             = ?,
2731         cn_source       = ?,
2732         cn_class        = ?,
2733         cn_item         = ?,
2734         cn_suffix       = ?,
2735         cn_sort         = ?,
2736         totalissues     = ?
2737         where biblioitemnumber = ?
2738         ";
2739     my $sth = $dbh->prepare($query);
2740     $sth->execute(
2741         $biblioitem->{'biblionumber'},
2742         $biblioitem->{'volume'},
2743         $biblioitem->{'number'},
2744         $biblioitem->{'itemtype'},
2745         $biblioitem->{'isbn'},
2746         $biblioitem->{'issn'},
2747         $biblioitem->{'publicationyear'},
2748         $biblioitem->{'publishercode'},
2749         $biblioitem->{'volumedate'},
2750         $biblioitem->{'volumedesc'},
2751         $biblioitem->{'collectiontitle'},
2752         $biblioitem->{'collectionissn'},
2753         $biblioitem->{'collectionvolume'},
2754         $biblioitem->{'editionstatement'},
2755         $biblioitem->{'editionresponsibility'},
2756         $biblioitem->{'illus'},
2757         $biblioitem->{'pages'},
2758         $biblioitem->{'bnotes'},
2759         $biblioitem->{'size'},
2760         $biblioitem->{'place'},
2761         $biblioitem->{'lccn'},
2762         $biblioitem->{'url'},
2763         $biblioitem->{'biblioitems.cn_source'},
2764         $biblioitem->{'cn_class'},
2765         $biblioitem->{'cn_item'},
2766         $biblioitem->{'cn_suffix'},
2767         $cn_sort,
2768         $biblioitem->{'totalissues'},
2769         $biblioitem->{'biblioitemnumber'}
2770     );
2771     if ( $dbh->errstr ) {
2772         $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
2773         warn $error;
2774     }
2775     return ($biblioitem->{'biblioitemnumber'},$error);
2776 }
2777
2778 =head2 _koha_add_biblioitem
2779
2780 =over 4
2781
2782 my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
2783
2784 Internal function to add a biblioitem
2785
2786 =back
2787
2788 =cut
2789
2790 sub _koha_add_biblioitem {
2791     my ( $dbh, $biblioitem ) = @_;
2792     my $error;
2793
2794     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2795     my $query =
2796     "INSERT INTO biblioitems SET
2797         biblionumber    = ?,
2798         volume          = ?,
2799         number          = ?,
2800         itemtype        = ?,
2801         isbn            = ?,
2802         issn            = ?,
2803         publicationyear = ?,
2804         publishercode   = ?,
2805         volumedate      = ?,
2806         volumedesc      = ?,
2807         collectiontitle = ?,
2808         collectionissn  = ?,
2809         collectionvolume= ?,
2810         editionstatement= ?,
2811         editionresponsibility = ?,
2812         illus           = ?,
2813         pages           = ?,
2814         notes           = ?,
2815         size            = ?,
2816         place           = ?,
2817         lccn            = ?,
2818         marc            = ?,
2819         url             = ?,
2820         cn_source       = ?,
2821         cn_class        = ?,
2822         cn_item         = ?,
2823         cn_suffix       = ?,
2824         cn_sort         = ?,
2825         totalissues     = ?
2826         ";
2827     my $sth = $dbh->prepare($query);
2828     $sth->execute(
2829         $biblioitem->{'biblionumber'},
2830         $biblioitem->{'volume'},
2831         $biblioitem->{'number'},
2832         $biblioitem->{'itemtype'},
2833         $biblioitem->{'isbn'},
2834         $biblioitem->{'issn'},
2835         $biblioitem->{'publicationyear'},
2836         $biblioitem->{'publishercode'},
2837         $biblioitem->{'volumedate'},
2838         $biblioitem->{'volumedesc'},
2839         $biblioitem->{'collectiontitle'},
2840         $biblioitem->{'collectionissn'},
2841         $biblioitem->{'collectionvolume'},
2842         $biblioitem->{'editionstatement'},
2843         $biblioitem->{'editionresponsibility'},
2844         $biblioitem->{'illus'},
2845         $biblioitem->{'pages'},
2846         $biblioitem->{'bnotes'},
2847         $biblioitem->{'size'},
2848         $biblioitem->{'place'},
2849         $biblioitem->{'lccn'},
2850         $biblioitem->{'marc'},
2851         $biblioitem->{'url'},
2852         $biblioitem->{'biblioitems.cn_source'},
2853         $biblioitem->{'cn_class'},
2854         $biblioitem->{'cn_item'},
2855         $biblioitem->{'cn_suffix'},
2856         $cn_sort,
2857         $biblioitem->{'totalissues'}
2858     );
2859     my $bibitemnum = $dbh->{'mysql_insertid'};
2860     if ( $dbh->errstr ) {
2861         $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
2862         warn $error;
2863     }
2864     $sth->finish();
2865     return ($bibitemnum,$error);
2866 }
2867
2868 =head2 _koha_delete_biblio
2869
2870 =over 4
2871
2872 $error = _koha_delete_biblio($dbh,$biblionumber);
2873
2874 Internal sub for deleting from biblio table -- also saves to deletedbiblio
2875
2876 C<$dbh> - the database handle
2877 C<$biblionumber> - the biblionumber of the biblio to be deleted
2878
2879 =back
2880
2881 =cut
2882
2883 # FIXME: add error handling
2884
2885 sub _koha_delete_biblio {
2886     my ( $dbh, $biblionumber ) = @_;
2887
2888     # get all the data for this biblio
2889     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
2890     $sth->execute($biblionumber);
2891
2892     if ( my $data = $sth->fetchrow_hashref ) {
2893
2894         # save the record in deletedbiblio
2895         # find the fields to save
2896         my $query = "INSERT INTO deletedbiblio SET ";
2897         my @bind  = ();
2898         foreach my $temp ( keys %$data ) {
2899             $query .= "$temp = ?,";
2900             push( @bind, $data->{$temp} );
2901         }
2902
2903         # replace the last , by ",?)"
2904         $query =~ s/\,$//;
2905         my $bkup_sth = $dbh->prepare($query);
2906         $bkup_sth->execute(@bind);
2907         $bkup_sth->finish;
2908
2909         # delete the biblio
2910         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
2911         $del_sth->execute($biblionumber);
2912         $del_sth->finish;
2913     }
2914     $sth->finish;
2915     return undef;
2916 }
2917
2918 =head2 _koha_delete_biblioitems
2919
2920 =over 4
2921
2922 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
2923
2924 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
2925
2926 C<$dbh> - the database handle
2927 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
2928
2929 =back
2930
2931 =cut
2932
2933 # FIXME: add error handling
2934
2935 sub _koha_delete_biblioitems {
2936     my ( $dbh, $biblioitemnumber ) = @_;
2937
2938     # get all the data for this biblioitem
2939     my $sth =
2940       $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
2941     $sth->execute($biblioitemnumber);
2942
2943     if ( my $data = $sth->fetchrow_hashref ) {
2944
2945         # save the record in deletedbiblioitems
2946         # find the fields to save
2947         my $query = "INSERT INTO deletedbiblioitems SET ";
2948         my @bind  = ();
2949         foreach my $temp ( keys %$data ) {
2950             $query .= "$temp = ?,";
2951             push( @bind, $data->{$temp} );
2952         }
2953
2954         # replace the last , by ",?)"
2955         $query =~ s/\,$//;
2956         my $bkup_sth = $dbh->prepare($query);
2957         $bkup_sth->execute(@bind);
2958         $bkup_sth->finish;
2959
2960         # delete the biblioitem
2961         my $del_sth =
2962           $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
2963         $del_sth->execute($biblioitemnumber);
2964         $del_sth->finish;
2965     }
2966     $sth->finish;
2967     return undef;
2968 }
2969
2970 =head1 UNEXPORTED FUNCTIONS
2971
2972 =head2 ModBiblioMarc
2973
2974     &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
2975     
2976     Add MARC data for a biblio to koha 
2977     
2978     Function exported, but should NOT be used, unless you really know what you're doing
2979
2980 =cut
2981
2982 sub ModBiblioMarc {
2983     
2984 # pass the MARC::Record to this function, and it will create the records in the marc field
2985     my ( $record, $biblionumber, $frameworkcode ) = @_;
2986     my $dbh = C4::Context->dbh;
2987     my @fields = $record->fields();
2988     if ( !$frameworkcode ) {
2989         $frameworkcode = "";
2990     }
2991     my $sth =
2992       $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
2993     $sth->execute( $frameworkcode, $biblionumber );
2994     $sth->finish;
2995     my $encoding = C4::Context->preference("marcflavour");
2996
2997     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
2998     if ( $encoding eq "UNIMARC" ) {
2999         my $string;
3000         if ( length($record->subfield( 100, "a" )) == 35 ) {
3001             $string = $record->subfield( 100, "a" );
3002             my $f100 = $record->field(100);
3003             $record->delete_field($f100);
3004         }
3005         else {
3006             $string = POSIX::strftime( "%Y%m%d", localtime );
3007             $string =~ s/\-//g;
3008             $string = sprintf( "%-*s", 35, $string );
3009         }
3010         substr( $string, 22, 6, "frey50" );
3011         unless ( $record->subfield( 100, "a" ) ) {
3012             $record->insert_grouped_field(
3013                 MARC::Field->new( 100, "", "", "a" => $string ) );
3014         }
3015     }
3016     ModZebra($biblionumber,"specialUpdate","biblioserver",$record);
3017     $sth =
3018       $dbh->prepare(
3019         "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
3020     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
3021         $biblionumber );
3022     $sth->finish;
3023     return $biblionumber;
3024 }
3025
3026 =head2 z3950_extended_services
3027
3028 z3950_extended_services($serviceType,$serviceOptions,$record);
3029
3030     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.
3031
3032 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
3033
3034 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
3035
3036     action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
3037
3038 and maybe
3039
3040     recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
3041     syntax => the record syntax (transfer syntax)
3042     databaseName = Database from connection object
3043
3044     To set serviceOptions, call set_service_options($serviceType)
3045
3046 C<$record> the record, if one is needed for the service type
3047
3048     A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
3049
3050 =cut
3051
3052 sub z3950_extended_services {
3053     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
3054
3055     # get our connection object
3056     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
3057
3058     # create a new package object
3059     my $Zpackage = $Zconn->package();
3060
3061     # set our options
3062     $Zpackage->option( action => $action );
3063
3064     if ( $serviceOptions->{'databaseName'} ) {
3065         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
3066     }
3067     if ( $serviceOptions->{'recordIdNumber'} ) {
3068         $Zpackage->option(
3069             recordIdNumber => $serviceOptions->{'recordIdNumber'} );
3070     }
3071     if ( $serviceOptions->{'recordIdOpaque'} ) {
3072         $Zpackage->option(
3073             recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
3074     }
3075
3076  # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
3077  #if ($serviceType eq 'itemorder') {
3078  #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
3079  #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
3080  #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
3081  #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
3082  #}
3083
3084     if ( $serviceOptions->{record} ) {
3085         $Zpackage->option( record => $serviceOptions->{record} );
3086
3087         # can be xml or marc
3088         if ( $serviceOptions->{'syntax'} ) {
3089             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
3090         }
3091     }
3092
3093     # send the request, handle any exception encountered
3094     eval { $Zpackage->send($serviceType) };
3095     if ( $@ && $@->isa("ZOOM::Exception") ) {
3096         return "error:  " . $@->code() . " " . $@->message() . "\n";
3097     }
3098
3099     # free up package resources
3100     $Zpackage->destroy();
3101 }
3102
3103 =head2 set_service_options
3104
3105 my $serviceOptions = set_service_options($serviceType);
3106
3107 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
3108
3109 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
3110
3111 =cut
3112
3113 sub set_service_options {
3114     my ($serviceType) = @_;
3115     my $serviceOptions;
3116
3117 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
3118 #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
3119
3120     if ( $serviceType eq 'commit' ) {
3121
3122         # nothing to do
3123     }
3124     if ( $serviceType eq 'create' ) {
3125
3126         # nothing to do
3127     }
3128     if ( $serviceType eq 'drop' ) {
3129         die "ERROR: 'drop' not currently supported (by Zebra)";
3130     }
3131     return $serviceOptions;
3132 }
3133
3134 1;
3135
3136 __END__
3137
3138 =head1 AUTHOR
3139
3140 Koha Developement team <info@koha.org>
3141
3142 Paul POULAIN paul.poulain@free.fr
3143
3144 Joshua Ferraro jmf@liblime.com
3145
3146 =cut