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