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