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