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