Adding ESCAPE=JS to TMPL_Tokenizer
[koha.git] / C4 / Biblio.pm
1 package C4::Biblio;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 use strict;
21 use warnings;
22 # use utf8;
23 use MARC::Record;
24 use MARC::File::USMARC;
25 # Force MARC::File::XML to use LibXML SAX Parser
26 #$XML::SAX::ParserPackage = "XML::LibXML::SAX";
27 use MARC::File::XML;
28 use ZOOM;
29 use POSIX qw(strftime);
30
31 use C4::Koha;
32 use C4::Dates qw/format_date/;
33 use C4::Log; # logaction
34 use C4::ClassSource;
35 use C4::Charset;
36 require C4::Heading;
37 require C4::Serials;
38
39 use vars qw($VERSION @ISA @EXPORT);
40
41 BEGIN {
42         $VERSION = 1.00;
43
44         require Exporter;
45         @ISA = qw( Exporter );
46
47         # to add biblios
48 # EXPORTED FUNCTIONS.
49         push @EXPORT, qw( 
50                 &AddBiblio
51         );
52
53         # to get something
54         push @EXPORT, qw(
55                 &GetBiblio
56                 &GetBiblioData
57                 &GetBiblioItemData
58                 &GetBiblioItemInfosOf
59                 &GetBiblioItemByBiblioNumber
60                 &GetBiblioFromItemNumber
61                 
62                 &GetISBDView
63
64                 &GetMarcNotes
65                 &GetMarcSubjects
66                 &GetMarcBiblio
67                 &GetMarcAuthors
68                 &GetMarcSeries
69                 GetMarcUrls
70                 &GetUsedMarcStructure
71                 &GetXmlBiblio
72         &GetCOinSBiblio
73
74                 &GetAuthorisedValueDesc
75                 &GetMarcStructure
76                 &GetMarcFromKohaField
77                 &GetFrameworkCode
78                 &GetPublisherNameFromIsbn
79                 &TransformKohaToMarc
80         );
81
82         # To modify something
83         push @EXPORT, qw(
84                 &ModBiblio
85                 &ModBiblioframework
86                 &ModZebra
87         );
88         # To delete something
89         push @EXPORT, qw(
90                 &DelBiblio
91         );
92
93     # To link headings in a bib record
94     # to authority records.
95     push @EXPORT, qw(
96         &LinkBibHeadingsToAuthorities
97     );
98
99         # Internal functions
100         # those functions are exported but should not be used
101         # they are usefull is few circumstances, so are exported.
102         # but don't use them unless you're a core developer ;-)
103         push @EXPORT, qw(
104                 &ModBiblioMarc
105         );
106         # Others functions
107         push @EXPORT, qw(
108                 &TransformMarcToKoha
109                 &TransformHtmlToMarc2
110                 &TransformHtmlToMarc
111                 &TransformHtmlToXml
112                 &PrepareItemrecordDisplay
113                 &GetNoZebraIndexes
114         );
115 }
116
117 =head1 NAME
118
119 C4::Biblio - cataloging management functions
120
121 =head1 DESCRIPTION
122
123 Biblio.pm contains functions for managing storage and editing of bibliographic data within Koha. Most of the functions in this module are used for cataloging records: adding, editing, or removing biblios, biblioitems, or items. Koha's stores bibliographic information in three places:
124
125 =over 4
126
127 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
128
129 =item 2. as raw MARC in the Zebra index and storage engine
130
131 =item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
132
133 =back
134
135 In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
136
137 Because the data isn't completely normalized there's a chance for information to get out of sync. The design choice to go with a un-normalized schema was driven by performance and stability concerns. However, if this occur, it can be considered as a bug : The API is (or should be) complete & the only entry point for all biblio/items managements.
138
139 =over 4
140
141 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
142
143 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
144
145 =back
146
147 Because of this design choice, the process of managing storage and editing is a bit convoluted. Historically, Biblio.pm's grown to an unmanagable size and as a result we have several types of functions currently:
148
149 =over 4
150
151 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
152
153 =item 2. _koha_* - low-level internal functions for managing the koha tables
154
155 =item 3. Marc management function : as the MARC record is stored in biblioitems.marc(xml), some subs dedicated to it's management are in this package. They should be used only internally by Biblio.pm, the only official entry points being AddBiblio, AddItem, ModBiblio, ModItem.
156
157 =item 4. Zebra functions used to update the Zebra index
158
159 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
160
161 =back
162
163 The MARC record (in biblioitems.marcxml) contains the complete marc record, including items. It also contains the biblionumber. That is the reason why it is not stored directly by AddBiblio, with all other fields . To save a biblio, we need to :
164
165 =over 4
166
167 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
168
169 =item 2. add the biblionumber and biblioitemnumber into the MARC records
170
171 =item 3. save the marc record
172
173 =back
174
175 When dealing with items, we must :
176
177 =over 4
178
179 =item 1. save the item in items table, that gives us an itemnumber
180
181 =item 2. add the itemnumber to the item MARC field
182
183 =item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
184
185 When modifying a biblio or an item, the behaviour is quite similar.
186
187 =back
188
189 =head1 EXPORTED FUNCTIONS
190
191 =head2 AddBiblio
192
193 =over 4
194
195 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
196
197 =back
198
199 Exported function (core API) for adding a new biblio to koha.
200
201 The first argument is a C<MARC::Record> object containing the
202 bib to add, while the second argument is the desired MARC
203 framework code.
204
205 This function also accepts a third, optional argument: a hashref
206 to additional options.  The only defined option is C<defer_marc_save>,
207 which if present and mapped to a true value, causes C<AddBiblio>
208 to omit the call to save the MARC in C<bibilioitems.marc>
209 and C<biblioitems.marcxml>  This option is provided B<only>
210 for the use of scripts such as C<bulkmarcimport.pl> that may need
211 to do some manipulation of the MARC record for item parsing before
212 saving it and which cannot afford the performance hit of saving
213 the MARC record twice.  Consequently, do not use that option
214 unless you can guarantee that C<ModBiblioMarc> will be called.
215
216 =cut
217
218 sub AddBiblio {
219     my $record = shift;
220     my $frameworkcode = shift;
221     my $options = @_ ? shift : undef;
222     my $defer_marc_save = 0;
223     if (defined $options and exists $options->{'defer_marc_save'} and $options->{'defer_marc_save'}) {
224         $defer_marc_save = 1;
225     }
226
227     my ($biblionumber,$biblioitemnumber,$error);
228     my $dbh = C4::Context->dbh;
229     # transform the data into koha-table style data
230     my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
231     ($biblionumber,$error) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
232     $olddata->{'biblionumber'} = $biblionumber;
233     ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $olddata );
234
235     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
236
237     # update MARC subfield that stores biblioitems.cn_sort
238     _koha_marc_update_biblioitem_cn_sort($record, $olddata, $frameworkcode);
239     
240     # now add the record
241     $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode ) unless $defer_marc_save;
242       
243     logaction("CATALOGUING", "ADD", $biblionumber, "biblio") if C4::Context->preference("CataloguingLog");
244     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') ? "&amp;rft.type=$genre" : "&amp;rft.genre=$genre";
1091
1092         # Setting datas
1093         $aulast     = $record->subfield('700','a');
1094         $aufirst    = $record->subfield('700','b');
1095         $oauthors   = "&amp;rft.au=$aufirst $aulast";
1096         # others authors
1097         if($record->field('200')){
1098             for my $au ($record->field('200')->subfield('g')){
1099                 $oauthors .= "&amp;rft.au=$au";
1100             }
1101         }
1102         $title      = ( $mtx eq 'dc' ) ? "&amp;rft.title=".$record->subfield('200','a') :
1103                                          "&amp;rft.title=".$record->subfield('200','a')."&amp;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     }
1109         else{
1110         # MARC21 need some improve
1111         my $fmts;
1112         $mtx = 'book';
1113         $genre = "&amp;rft.genre=book";
1114
1115         # Setting datas
1116         if ($record->field('100')) {
1117             $oauthors .= "&amp;rft.au=".$record->subfield('100','a');
1118         }
1119         # others authors
1120         if($record->field('700')){
1121             for my $au ($record->field('700')->subfield('a')){
1122                 $oauthors .= "&amp;rft.au=$au";
1123             }
1124         }
1125         $title      = "&amp;rft.btitle=".$record->subfield('245','a');
1126         $subtitle   = $record->subfield('245', 'b') || '';
1127         $title .= $subtitle;
1128         $pubyear    = $record->subfield('260', 'c') || '';
1129         $publisher  = $record->subfield('260', 'b') || '';
1130         $isbn       = $record->subfield('020', 'a') || '';
1131         $issn       = $record->subfield('022', 'a') || '';
1132
1133     }
1134     $coins_value = "ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3A$mtx$genre$title&amp;rft.isbn=$isbn&amp;rft.issn=$issn&amp;rft.aulast=$aulast&amp;rft.aufirst=$aufirst$oauthors&amp;rft.pub=$publisher&amp;rft.date=$pubyear";
1135     $coins_value =~ s/(\ |&[^a])/\+/g;
1136     #<!-- TMPL_VAR NAME="ocoins_format" -->&amp;rft.au=<!-- TMPL_VAR NAME="author" -->&amp;rft.btitle=<!-- TMPL_VAR NAME="title" -->&amp;rft.date=<!-- TMPL_VAR NAME="publicationyear" -->&amp;rft.pages=<!-- TMPL_VAR NAME="pages" -->&amp;rft.isbn=<!-- TMPL_VAR NAME=amazonisbn -->&amp;rft.aucorp=&amp;rft.place=<!-- TMPL_VAR NAME="place" -->&amp;rft.pub=<!-- TMPL_VAR NAME="publishercode" -->&amp;rft.edition=<!-- TMPL_VAR NAME="edition" -->&amp;rft.series=<!-- TMPL_VAR NAME="series" -->&amp;rft.genre="
1137         }
1138     return $coins_value;
1139 }
1140
1141 =head2 GetAuthorisedValueDesc
1142
1143 =over 4
1144
1145 my $subfieldvalue =get_authorised_value_desc(
1146     $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category);
1147 Retrieve the complete description for a given authorised value.
1148
1149 Now takes $category and $value pair too.
1150 my $auth_value_desc =GetAuthorisedValueDesc(
1151     '','', 'DVD' ,'','','CCODE');
1152
1153 =back
1154
1155 =cut
1156
1157 sub GetAuthorisedValueDesc {
1158     my ( $tag, $subfield, $value, $framework, $tagslib, $category ) = @_;
1159     my $dbh = C4::Context->dbh;
1160
1161     if (!$category) {
1162
1163         return $value unless defined $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1164
1165 #---- branch
1166         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1167             return C4::Branch::GetBranchName($value);
1168         }
1169
1170 #---- itemtypes
1171         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1172             return getitemtypeinfo($value)->{description};
1173         }
1174
1175 #---- "true" authorized value
1176         $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'}
1177     }
1178
1179     if ( $category ne "" ) {
1180         my $sth =
1181             $dbh->prepare(
1182                     "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
1183                     );
1184         $sth->execute( $category, $value );
1185         my $data = $sth->fetchrow_hashref;
1186         return $data->{'lib'};
1187     }
1188     else {
1189         return $value;    # if nothing is found return the original value
1190     }
1191 }
1192
1193 =head2 GetMarcNotes
1194
1195 =over 4
1196
1197 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1198 Get all notes from the MARC record and returns them in an array.
1199 The note are stored in differents places depending on MARC flavour
1200
1201 =back
1202
1203 =cut
1204
1205 sub GetMarcNotes {
1206     my ( $record, $marcflavour ) = @_;
1207     my $scope;
1208     if ( $marcflavour eq "MARC21" ) {
1209         $scope = '5..';
1210     }
1211     else {    # assume unimarc if not marc21
1212         $scope = '3..';
1213     }
1214     my @marcnotes;
1215     my $note = "";
1216     my $tag  = "";
1217     my $marcnote;
1218     foreach my $field ( $record->field($scope) ) {
1219         my $value = $field->as_string();
1220         $value =~ s/\n/<br \/>/g ;
1221
1222         if ( $note ne "" ) {
1223             $marcnote = { marcnote => $note, };
1224             push @marcnotes, $marcnote;
1225             $note = $value;
1226         }
1227         if ( $note ne $value ) {
1228             $note = $note . " " . $value;
1229         }
1230     }
1231
1232     if ( $note ) {
1233         $marcnote = { marcnote => $note };
1234         push @marcnotes, $marcnote;    #load last tag into array
1235     }
1236     return \@marcnotes;
1237 }    # end GetMarcNotes
1238
1239 =head2 GetMarcSubjects
1240
1241 =over 4
1242
1243 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1244 Get all subjects from the MARC record and returns them in an array.
1245 The subjects are stored in differents places depending on MARC flavour
1246
1247 =back
1248
1249 =cut
1250
1251 sub GetMarcSubjects {
1252     my ( $record, $marcflavour ) = @_;
1253     my ( $mintag, $maxtag );
1254     if ( $marcflavour eq "MARC21" ) {
1255         $mintag = "600";
1256         $maxtag = "699";
1257     }
1258     else {    # assume unimarc if not marc21
1259         $mintag = "600";
1260         $maxtag = "611";
1261     }
1262     
1263     my @marcsubjects;
1264     my $subject = "";
1265     my $subfield = "";
1266     my $marcsubject;
1267
1268     foreach my $field ( $record->field('6..' )) {
1269         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1270         my @subfields_loop;
1271         my @subfields = $field->subfields();
1272         my $counter = 0;
1273         my @link_loop;
1274         # if there is an authority link, build the link with an= subfield9
1275         my $f9found = 0;
1276         for my $subject_subfield (@subfields ) {
1277             # don't load unimarc subfields 2,3,4,5
1278             next if (($marcflavour eq "UNIMARC") and ($subject_subfield->[0] =~ /2|3|4|5/ ) );
1279             # don't load MARC21 subfields 2 (FIXME: any more subfields??)
1280             next if (($marcflavour eq "MARC21")  and ($subject_subfield->[0] =~ /2/ ) );
1281             my $code = $subject_subfield->[0];
1282             my $value = $subject_subfield->[1];
1283             my $linkvalue = $value;
1284             $linkvalue =~ s/(\(|\))//g;
1285             my $operator = " and " unless $counter==0;
1286             if ($code eq 9) {
1287                 $f9found = 1;
1288                 @link_loop = ({'limit' => 'an' ,link => "$linkvalue" });
1289             } else {
1290                 if (not $f9found) {
1291                         push @link_loop, {'limit' => 'su', link => $linkvalue, operator => $operator };
1292                 }
1293             }
1294             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1295             # ignore $9
1296             my @this_link_loop = @link_loop;
1297             push @subfields_loop, {code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($subject_subfield->[0] eq 9 );
1298             $counter++;
1299         }
1300                 
1301         push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
1302         
1303     }
1304         return \@marcsubjects;
1305 }  #end getMARCsubjects
1306
1307 =head2 GetMarcAuthors
1308
1309 =over 4
1310
1311 authors = GetMarcAuthors($record,$marcflavour);
1312 Get all authors from the MARC record and returns them in an array.
1313 The authors are stored in differents places depending on MARC flavour
1314
1315 =back
1316
1317 =cut
1318
1319 sub GetMarcAuthors {
1320     my ( $record, $marcflavour ) = @_;
1321     my ( $mintag, $maxtag );
1322     # tagslib useful for UNIMARC author reponsabilities
1323     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.
1324     if ( $marcflavour eq "MARC21" ) {
1325         $mintag = "700";
1326         $maxtag = "720"; 
1327     }
1328     elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
1329         $mintag = "700";
1330         $maxtag = "712";
1331     }
1332     else {
1333         return;
1334     }
1335     my @marcauthors;
1336
1337     foreach my $field ( $record->fields ) {
1338         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1339         my @subfields_loop;
1340         my @link_loop;
1341         my @subfields = $field->subfields();
1342         my $count_auth = 0;
1343         # if there is an authority link, build the link with Koha-Auth-Number: subfield9
1344         my $subfield9 = $field->subfield('9');
1345         for my $authors_subfield (@subfields) {
1346             # don't load unimarc subfields 3, 5
1347             next if ($marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~ /3|5/ ) );
1348             my $subfieldcode = $authors_subfield->[0];
1349             my $value = $authors_subfield->[1];
1350             my $linkvalue = $value;
1351             $linkvalue =~ s/(\(|\))//g;
1352             my $operator = " and " unless $count_auth==0;
1353             # if we have an authority link, use that as the link, otherwise use standard searching
1354             if ($subfield9) {
1355                 @link_loop = ({'limit' => 'an' ,link => "$subfield9" });
1356             }
1357             else {
1358                 # reset $linkvalue if UNIMARC author responsibility
1359                 if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq "4")) {
1360                     $linkvalue = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
1361                 }
1362                 push @link_loop, {'limit' => 'au', link => $linkvalue, operator => $operator };
1363             }
1364             $value = GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ) if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~/4/));
1365             my @this_link_loop = @link_loop;
1366             my $separator = C4::Context->preference("authoritysep") unless $count_auth==0;
1367             push @subfields_loop, {code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($authors_subfield->[0] eq '9' );
1368             $count_auth++;
1369         }
1370         push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
1371     }
1372     return \@marcauthors;
1373 }
1374
1375 =head2 GetMarcUrls
1376
1377 =over 4
1378
1379 $marcurls = GetMarcUrls($record,$marcflavour);
1380 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1381 Assumes web resources (not uncommon in MARC21 to omit resource type ind) 
1382
1383 =back
1384
1385 =cut
1386
1387 sub GetMarcUrls {
1388     my ( $record, $marcflavour ) = @_;
1389
1390     my @marcurls;
1391     for my $field ( $record->field('856') ) {
1392         my $marcurl;
1393         my @notes;
1394         for my $note ( $field->subfield('z') ) {
1395             push @notes, { note => $note };
1396         }
1397         my @urls = $field->subfield('u');
1398         foreach my $url (@urls) {
1399             if ( $marcflavour eq 'MARC21' ) {
1400                 my $s3   = $field->subfield('3');
1401                 my $link = $field->subfield('y');
1402                 unless ( $url =~ /^\w+:/ ) {
1403                     if ( $field->indicator(1) eq '7' ) {
1404                         $url = $field->subfield('2') . "://" . $url;
1405                     } elsif ( $field->indicator(1) eq '1' ) {
1406                         $url = 'ftp://' . $url;
1407                     } else {
1408                         #  properly, this should be if ind1=4,
1409                         #  however we will assume http protocol since we're building a link.
1410                         $url = 'http://' . $url;
1411                     }
1412                 }
1413                 # TODO handle ind 2 (relationship)
1414                 $marcurl = {
1415                     MARCURL => $url,
1416                     notes   => \@notes,
1417                 };
1418                 $marcurl->{'linktext'} = $link || $s3 || C4::Context->preference('URLLinkText') || $url;
1419                 $marcurl->{'part'} = $s3 if ($link);
1420                 $marcurl->{'toc'} = 1 if ( defined($s3) && $s3 =~ /^[Tt]able/ );
1421             } else {
1422                 $marcurl->{'linktext'} = $field->subfield('2') || C4::Context->preference('URLLinkText') || $url;
1423                 $marcurl->{'MARCURL'} = $url;
1424             }
1425             push @marcurls, $marcurl;
1426         }
1427     }
1428     return \@marcurls;
1429 }
1430
1431 =head2 GetMarcSeries
1432
1433 =over 4
1434
1435 $marcseriesarray = GetMarcSeries($record,$marcflavour);
1436 Get all series from the MARC record and returns them in an array.
1437 The series are stored in differents places depending on MARC flavour
1438
1439 =back
1440
1441 =cut
1442
1443 sub GetMarcSeries {
1444     my ($record, $marcflavour) = @_;
1445     my ($mintag, $maxtag);
1446     if ($marcflavour eq "MARC21") {
1447         $mintag = "440";
1448         $maxtag = "490";
1449     } else {           # assume unimarc if not marc21
1450         $mintag = "600";
1451         $maxtag = "619";
1452     }
1453
1454     my @marcseries;
1455     my $subjct = "";
1456     my $subfield = "";
1457     my $marcsubjct;
1458
1459     foreach my $field ($record->field('440'), $record->field('490')) {
1460         my @subfields_loop;
1461         #my $value = $field->subfield('a');
1462         #$marcsubjct = {MARCSUBJCT => $value,};
1463         my @subfields = $field->subfields();
1464         #warn "subfields:".join " ", @$subfields;
1465         my $counter = 0;
1466         my @link_loop;
1467         for my $series_subfield (@subfields) {
1468             my $volume_number;
1469             undef $volume_number;
1470             # see if this is an instance of a volume
1471             if ($series_subfield->[0] eq 'v') {
1472                 $volume_number=1;
1473             }
1474
1475             my $code = $series_subfield->[0];
1476             my $value = $series_subfield->[1];
1477             my $linkvalue = $value;
1478             $linkvalue =~ s/(\(|\))//g;
1479             my $operator = " and " unless $counter==0;
1480             push @link_loop, {link => $linkvalue, operator => $operator };
1481             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1482             if ($volume_number) {
1483             push @subfields_loop, {volumenum => $value};
1484             }
1485             else {
1486             push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number};
1487             }
1488             $counter++;
1489         }
1490         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1491         #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1492         #push @marcsubjcts, $marcsubjct;
1493         #$subjct = $value;
1494
1495     }
1496     my $marcseriessarray=\@marcseries;
1497     return $marcseriessarray;
1498 }  #end getMARCseriess
1499
1500 =head2 GetFrameworkCode
1501
1502 =over 4
1503
1504     $frameworkcode = GetFrameworkCode( $biblionumber )
1505
1506 =back
1507
1508 =cut
1509
1510 sub GetFrameworkCode {
1511     my ( $biblionumber ) = @_;
1512     my $dbh = C4::Context->dbh;
1513     my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
1514     $sth->execute($biblionumber);
1515     my ($frameworkcode) = $sth->fetchrow;
1516     return $frameworkcode;
1517 }
1518
1519 =head2 GetPublisherNameFromIsbn
1520
1521     $name = GetPublishercodeFromIsbn($isbn);
1522     if(defined $name){
1523         ...
1524     }
1525
1526 =cut
1527
1528 sub GetPublisherNameFromIsbn($){
1529     my $isbn = shift;
1530     $isbn =~ s/[- _]//g;
1531     $isbn =~ s/^0*//;
1532     my @codes = (split '-', DisplayISBN($isbn));
1533     my $code = $codes[0].$codes[1].$codes[2];
1534     my $dbh  = C4::Context->dbh;
1535     my $query = qq{
1536         SELECT distinct publishercode
1537         FROM   biblioitems
1538         WHERE  isbn LIKE ?
1539         AND    publishercode IS NOT NULL
1540         LIMIT 1
1541     };
1542     my $sth = $dbh->prepare($query);
1543     $sth->execute("$code%");
1544     my $name = $sth->fetchrow;
1545     return $name if length $name;
1546     return undef;
1547 }
1548
1549 =head2 TransformKohaToMarc
1550
1551 =over 4
1552
1553     $record = TransformKohaToMarc( $hash )
1554     This function builds partial MARC::Record from a hash
1555     Hash entries can be from biblio or biblioitems.
1556     This function is called in acquisition module, to create a basic catalogue entry from user entry
1557
1558 =back
1559
1560 =cut
1561
1562 sub TransformKohaToMarc {
1563     my ( $hash ) = @_;
1564     my $sth = C4::Context->dbh->prepare(
1565         "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
1566     );
1567     my $record = MARC::Record->new();
1568     SetMarcUnicodeFlag($record, C4::Context->preference("marcflavour"));
1569     foreach (keys %{$hash}) {
1570         &TransformKohaToMarcOneField( $sth, $record, $_, $hash->{$_}, '' );
1571     }
1572     return $record;
1573 }
1574
1575 =head2 TransformKohaToMarcOneField
1576
1577 =over 4
1578
1579     $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
1580
1581 =back
1582
1583 =cut
1584
1585 sub TransformKohaToMarcOneField {
1586     my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
1587     $frameworkcode='' unless $frameworkcode;
1588     my $tagfield;
1589     my $tagsubfield;
1590
1591     if ( !defined $sth ) {
1592         my $dbh = C4::Context->dbh;
1593         $sth = $dbh->prepare(
1594             "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
1595         );
1596     }
1597     $sth->execute( $frameworkcode, $kohafieldname );
1598     if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
1599         my $tag = $record->field($tagfield);
1600         if ($tag) {
1601             $tag->update( $tagsubfield => $value );
1602             $record->delete_field($tag);
1603             $record->insert_fields_ordered($tag);
1604         }
1605         else {
1606             $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
1607         }
1608     }
1609     return $record;
1610 }
1611
1612 =head2 TransformHtmlToXml
1613
1614 =over 4
1615
1616 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type )
1617
1618 $auth_type contains :
1619 - nothing : rebuild a biblio, un UNIMARC the encoding is in 100$a pos 26/27
1620 - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
1621 - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
1622
1623 =back
1624
1625 =cut
1626
1627 sub TransformHtmlToXml {
1628     my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
1629     my $xml = MARC::File::XML::header('UTF-8');
1630     $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
1631     MARC::File::XML->default_record_format($auth_type);
1632     # in UNIMARC, field 100 contains the encoding
1633     # check that there is one, otherwise the 
1634     # MARC::Record->new_from_xml will fail (and Koha will die)
1635     my $unimarc_and_100_exist=0;
1636     $unimarc_and_100_exist=1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
1637     my $prevvalue;
1638     my $prevtag = -1;
1639     my $first   = 1;
1640     my $j       = -1;
1641     for ( my $i = 0 ; $i < @$tags ; $i++ ) {
1642         if (C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a") {
1643             # if we have a 100 field and it's values are not correct, skip them.
1644             # if we don't have any valid 100 field, we will create a default one at the end
1645             my $enc = substr( @$values[$i], 26, 2 );
1646             if ($enc eq '01' or $enc eq '50' or $enc eq '03') {
1647                 $unimarc_and_100_exist=1;
1648             } else {
1649                 next;
1650             }
1651         }
1652         @$values[$i] =~ s/&/&amp;/g;
1653         @$values[$i] =~ s/</&lt;/g;
1654         @$values[$i] =~ s/>/&gt;/g;
1655         @$values[$i] =~ s/"/&quot;/g;
1656         @$values[$i] =~ s/'/&apos;/g;
1657 #         if ( !utf8::is_utf8( @$values[$i] ) ) {
1658 #             utf8::decode( @$values[$i] );
1659 #         }
1660         if ( ( @$tags[$i] ne $prevtag ) ) {
1661             $j++ unless ( @$tags[$i] eq "" );
1662             if ( !$first ) {
1663                 $xml .= "</datafield>\n";
1664                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
1665                     && ( @$values[$i] ne "" ) )
1666                 {
1667                     my $ind1 = substr( @$indicator[$j], 0, 1 );
1668                     my $ind2;
1669                     if ( @$indicator[$j] ) {
1670                         $ind2 = substr( @$indicator[$j], 1, 1 );
1671                     }
1672                     else {
1673                         warn "Indicator in @$tags[$i] is empty";
1674                         $ind2 = " ";
1675                     }
1676                     $xml .=
1677 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1678                     $xml .=
1679 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1680                     $first = 0;
1681                 }
1682                 else {
1683                     $first = 1;
1684                 }
1685             }
1686             else {
1687                 if ( @$values[$i] ne "" ) {
1688
1689                     # leader
1690                     if ( @$tags[$i] eq "000" ) {
1691                         $xml .= "<leader>@$values[$i]</leader>\n";
1692                         $first = 1;
1693
1694                         # rest of the fixed fields
1695                     }
1696                     elsif ( @$tags[$i] < 10 ) {
1697                         $xml .=
1698 "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
1699                         $first = 1;
1700                     }
1701                     else {
1702                         my $ind1 = substr( @$indicator[$j], 0, 1 );
1703                         my $ind2 = substr( @$indicator[$j], 1, 1 );
1704                         $xml .=
1705 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1706                         $xml .=
1707 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1708                         $first = 0;
1709                     }
1710                 }
1711             }
1712         }
1713         else {    # @$tags[$i] eq $prevtag
1714             if ( @$values[$i] eq "" ) {
1715             }
1716             else {
1717                 if ($first) {
1718                     my $ind1 = substr( @$indicator[$j], 0, 1 );
1719                     my $ind2 = substr( @$indicator[$j], 1, 1 );
1720                     $xml .=
1721 "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1722                     $first = 0;
1723                 }
1724                 $xml .=
1725 "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1726             }
1727         }
1728         $prevtag = @$tags[$i];
1729     }
1730     $xml .= "</datafield>\n" if @$tags > 0;
1731     if (C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist) {
1732 #     warn "SETTING 100 for $auth_type";
1733         my $string = strftime( "%Y%m%d", localtime(time) );
1734         # set 50 to position 26 is biblios, 13 if authorities
1735         my $pos=26;
1736         $pos=13 if $auth_type eq 'UNIMARCAUTH';
1737         $string = sprintf( "%-*s", 35, $string );
1738         substr( $string, $pos , 6, "50" );
1739         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
1740         $xml .= "<subfield code=\"a\">$string</subfield>\n";
1741         $xml .= "</datafield>\n";
1742     }
1743     $xml .= MARC::File::XML::footer();
1744     return $xml;
1745 }
1746
1747 =head2 TransformHtmlToMarc
1748
1749     L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
1750     L<$params> is a ref to an array as below:
1751     {
1752         'tag_010_indicator1_531951' ,
1753         'tag_010_indicator2_531951' ,
1754         'tag_010_code_a_531951_145735' ,
1755         'tag_010_subfield_a_531951_145735' ,
1756         'tag_200_indicator1_873510' ,
1757         'tag_200_indicator2_873510' ,
1758         'tag_200_code_a_873510_673465' ,
1759         'tag_200_subfield_a_873510_673465' ,
1760         'tag_200_code_b_873510_704318' ,
1761         'tag_200_subfield_b_873510_704318' ,
1762         'tag_200_code_e_873510_280822' ,
1763         'tag_200_subfield_e_873510_280822' ,
1764         'tag_200_code_f_873510_110730' ,
1765         'tag_200_subfield_f_873510_110730' ,
1766     }
1767     L<$cgi> is the CGI object which containts the value.
1768     L<$record> is the MARC::Record object.
1769
1770 =cut
1771
1772 sub TransformHtmlToMarc {
1773     my $params = shift;
1774     my $cgi    = shift;
1775
1776     # explicitly turn on the UTF-8 flag for all
1777     # 'tag_' parameters to avoid incorrect character
1778     # conversion later on
1779     my $cgi_params = $cgi->Vars;
1780     foreach my $param_name (keys %$cgi_params) {
1781         if ($param_name =~ /^tag_/) {
1782             my $param_value = $cgi_params->{$param_name};
1783             if (utf8::decode($param_value)) {
1784                 $cgi_params->{$param_name} = $param_value;
1785             } 
1786             # FIXME - need to do something if string is not valid UTF-8
1787         }
1788     }
1789    
1790     # creating a new record
1791     my $record  = MARC::Record->new();
1792     my $i=0;
1793     my @fields;
1794     while ($params->[$i]){ # browse all CGI params
1795         my $param = $params->[$i];
1796         my $newfield=0;
1797         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
1798         if ($param eq 'biblionumber') {
1799             my ( $biblionumbertagfield, $biblionumbertagsubfield ) =
1800                 &GetMarcFromKohaField( "biblio.biblionumber", '' );
1801             if ($biblionumbertagfield < 10) {
1802                 $newfield = MARC::Field->new(
1803                     $biblionumbertagfield,
1804                     $cgi->param($param),
1805                 );
1806             } else {
1807                 $newfield = MARC::Field->new(
1808                     $biblionumbertagfield,
1809                     '',
1810                     '',
1811                     "$biblionumbertagsubfield" => $cgi->param($param),
1812                 );
1813             }
1814             push @fields,$newfield if($newfield);
1815         } 
1816         elsif ($param =~ /^tag_(\d*)_indicator1_/){ # new field start when having 'input name="..._indicator1_..."
1817             my $tag  = $1;
1818             
1819             my $ind1 = substr($cgi->param($param),0,1);
1820             my $ind2 = substr($cgi->param($params->[$i+1]),0,1);
1821             $newfield=0;
1822             my $j=$i+2;
1823             
1824             if($tag < 10){ # no code for theses fields
1825     # in MARC editor, 000 contains the leader.
1826                 if ($tag eq '000' ) {
1827                     $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==24;
1828     # between 001 and 009 (included)
1829                 } elsif ($cgi->param($params->[$j+1]) ne '') {
1830                     $newfield = MARC::Field->new(
1831                         $tag,
1832                         $cgi->param($params->[$j+1]),
1833                     );
1834                 }
1835     # > 009, deal with subfields
1836             } else {
1837                 while(defined $params->[$j] && $params->[$j] =~ /_code_/){ # browse all it's subfield
1838                     my $inner_param = $params->[$j];
1839                     if ($newfield){
1840                         if($cgi->param($params->[$j+1]) ne ''){  # only if there is a value (code => value)
1841                             $newfield->add_subfields(
1842                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
1843                             );
1844                         }
1845                     } else {
1846                         if ( $cgi->param($params->[$j+1]) ne '' ) { # creating only if there is a value (code => value)
1847                             $newfield = MARC::Field->new(
1848                                 $tag,
1849                                 ''.$ind1,
1850                                 ''.$ind2,
1851                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1]),
1852                             );
1853                         }
1854                     }
1855                     $j+=2;
1856                 }
1857             }
1858             push @fields,$newfield if($newfield);
1859         }
1860         $i++;
1861     }
1862     
1863     $record->append_fields(@fields);
1864     return $record;
1865 }
1866
1867 # cache inverted MARC field map
1868 our $inverted_field_map;
1869
1870 =head2 TransformMarcToKoha
1871
1872 =over 4
1873
1874     $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
1875
1876 =back
1877
1878 Extract data from a MARC bib record into a hashref representing
1879 Koha biblio, biblioitems, and items fields. 
1880
1881 =cut
1882 sub TransformMarcToKoha {
1883     my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
1884
1885     my $result;
1886     $limit_table=$limit_table||0;
1887     $frameworkcode = '' unless defined $frameworkcode;
1888     
1889     unless (defined $inverted_field_map) {
1890         $inverted_field_map = _get_inverted_marc_field_map();
1891     }
1892
1893     my %tables = ();
1894     if ( defined $limit_table && $limit_table eq 'items') {
1895         $tables{'items'} = 1;
1896     } else {
1897         $tables{'items'} = 1;
1898         $tables{'biblio'} = 1;
1899         $tables{'biblioitems'} = 1;
1900     }
1901
1902     # traverse through record
1903     MARCFIELD: foreach my $field ($record->fields()) {
1904         my $tag = $field->tag();
1905         next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
1906         if ($field->is_control_field()) {
1907             my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
1908             ENTRY: foreach my $entry (@{ $kohafields }) {
1909                 my ($subfield, $table, $column) = @{ $entry };
1910                 next ENTRY unless exists $tables{$table};
1911                 my $key = _disambiguate($table, $column);
1912                 if ($result->{$key}) {
1913                     unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($field->data() eq "")) {
1914                         $result->{$key} .= " | " . $field->data();
1915                     }
1916                 } else {
1917                     $result->{$key} = $field->data();
1918                 }
1919             }
1920         } else {
1921             # deal with subfields
1922             MARCSUBFIELD: foreach my $sf ($field->subfields()) {
1923                 my $code = $sf->[0];
1924                 next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
1925                 my $value = $sf->[1];
1926                 SFENTRY: foreach my $entry (@{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} }) {
1927                     my ($table, $column) = @{ $entry };
1928                     next SFENTRY unless exists $tables{$table};
1929                     my $key = _disambiguate($table, $column);
1930                     if ($result->{$key}) {
1931                         unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
1932                             $result->{$key} .= " | " . $value;
1933                         }
1934                     } else {
1935                         $result->{$key} = $value;
1936                     }
1937                 }
1938             }
1939         }
1940     }
1941
1942     # modify copyrightdate to keep only the 1st year found
1943     if (exists $result->{'copyrightdate'}) {
1944         my $temp = $result->{'copyrightdate'};
1945         $temp =~ m/c(\d\d\d\d)/;
1946         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
1947             $result->{'copyrightdate'} = $1;
1948         }
1949         else {                      # if no cYYYY, get the 1st date.
1950             $temp =~ m/(\d\d\d\d)/;
1951             $result->{'copyrightdate'} = $1;
1952         }
1953     }
1954
1955     # modify publicationyear to keep only the 1st year found
1956     if (exists $result->{'publicationyear'}) {
1957         my $temp = $result->{'publicationyear'};
1958         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
1959             $result->{'publicationyear'} = $1;
1960         }
1961         else {                      # if no cYYYY, get the 1st date.
1962             $temp =~ m/(\d\d\d\d)/;
1963             $result->{'publicationyear'} = $1;
1964         }
1965     }
1966
1967     return $result;
1968 }
1969
1970 sub _get_inverted_marc_field_map {
1971     my $field_map = {};
1972     my $relations = C4::Context->marcfromkohafield;
1973
1974     foreach my $frameworkcode (keys %{ $relations }) {
1975         foreach my $kohafield (keys %{ $relations->{$frameworkcode} }) {
1976             next unless @{ $relations->{$frameworkcode}->{$kohafield} }; # not all columns are mapped to MARC tag & subfield
1977             my $tag = $relations->{$frameworkcode}->{$kohafield}->[0];
1978             my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
1979             my ($table, $column) = split /[.]/, $kohafield, 2;
1980             push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
1981             push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
1982         }
1983     }
1984     return $field_map;
1985 }
1986
1987 =head2 _disambiguate
1988
1989 =over 4
1990
1991 $newkey = _disambiguate($table, $field);
1992
1993 This is a temporary hack to distinguish between the
1994 following sets of columns when using TransformMarcToKoha.
1995
1996 items.cn_source & biblioitems.cn_source
1997 items.cn_sort & biblioitems.cn_sort
1998
1999 Columns that are currently NOT distinguished (FIXME
2000 due to lack of time to fully test) are:
2001
2002 biblio.notes and biblioitems.notes
2003 biblionumber
2004 timestamp
2005 biblioitemnumber
2006
2007 FIXME - this is necessary because prefixing each column
2008 name with the table name would require changing lots
2009 of code and templates, and exposing more of the DB
2010 structure than is good to the UI templates, particularly
2011 since biblio and bibloitems may well merge in a future
2012 version.  In the future, it would also be good to 
2013 separate DB access and UI presentation field names
2014 more.
2015
2016 =back
2017
2018 =cut
2019
2020 sub _disambiguate {
2021     my ($table, $column) = @_;
2022     if ($column eq "cn_sort" or $column eq "cn_source") {
2023         return $table . '.' . $column;
2024     } else {
2025         return $column;
2026     }
2027
2028 }
2029
2030 =head2 get_koha_field_from_marc
2031
2032 =over 4
2033
2034 $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2035
2036 Internal function to map data from the MARC record to a specific non-MARC field.
2037 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2038
2039 =back
2040
2041 =cut
2042
2043 sub get_koha_field_from_marc {
2044     my ($koha_table,$koha_column,$record,$frameworkcode) = @_;
2045     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table.'.'.$koha_column, $frameworkcode );  
2046     my $kohafield;
2047     foreach my $field ( $record->field($tagfield) ) {
2048         if ( $field->tag() < 10 ) {
2049             if ( $kohafield ) {
2050                 $kohafield .= " | " . $field->data();
2051             }
2052             else {
2053                 $kohafield = $field->data();
2054             }
2055         }
2056         else {
2057             if ( $field->subfields ) {
2058                 my @subfields = $field->subfields();
2059                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2060                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2061                         if ( $kohafield ) {
2062                             $kohafield .=
2063                               " | " . $subfields[$subfieldcount][1];
2064                         }
2065                         else {
2066                             $kohafield =
2067                               $subfields[$subfieldcount][1];
2068                         }
2069                     }
2070                 }
2071             }
2072         }
2073     }
2074     return $kohafield;
2075
2076
2077
2078 =head2 TransformMarcToKohaOneField
2079
2080 =over 4
2081
2082 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2083
2084 =back
2085
2086 =cut
2087
2088 sub TransformMarcToKohaOneField {
2089
2090     # FIXME ? if a field has a repeatable subfield that is used in old-db,
2091     # only the 1st will be retrieved...
2092     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2093     my $res = "";
2094     my ( $tagfield, $subfield ) =
2095       GetMarcFromKohaField( $kohatable . "." . $kohafield,
2096         $frameworkcode );
2097     foreach my $field ( $record->field($tagfield) ) {
2098         if ( $field->tag() < 10 ) {
2099             if ( $result->{$kohafield} ) {
2100                 $result->{$kohafield} .= " | " . $field->data();
2101             }
2102             else {
2103                 $result->{$kohafield} = $field->data();
2104             }
2105         }
2106         else {
2107             if ( $field->subfields ) {
2108                 my @subfields = $field->subfields();
2109                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2110                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2111                         if ( $result->{$kohafield} ) {
2112                             $result->{$kohafield} .=
2113                               " | " . $subfields[$subfieldcount][1];
2114                         }
2115                         else {
2116                             $result->{$kohafield} =
2117                               $subfields[$subfieldcount][1];
2118                         }
2119                     }
2120                 }
2121             }
2122         }
2123     }
2124     return $result;
2125 }
2126
2127 =head1  OTHER FUNCTIONS
2128
2129
2130 =head2 PrepareItemrecordDisplay
2131
2132 =over 4
2133
2134 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber);
2135
2136 Returns a hash with all the fields for Display a given item data in a template
2137
2138 =back
2139
2140 =cut
2141
2142 sub PrepareItemrecordDisplay {
2143
2144     my ( $bibnum, $itemnum, $defaultvalues ) = @_;
2145
2146     my $dbh = C4::Context->dbh;
2147     my $frameworkcode = &GetFrameworkCode( $bibnum );
2148     my ( $itemtagfield, $itemtagsubfield ) =
2149       &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2150     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2151     my $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum) if ($itemnum);
2152     my @loop_data;
2153     my $authorised_values_sth =
2154       $dbh->prepare(
2155 "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"
2156       );
2157     foreach my $tag ( sort keys %{$tagslib} ) {
2158         my $previous_tag = '';
2159         if ( $tag ne '' ) {
2160             # loop through each subfield
2161             my $cntsubf;
2162             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2163                 next if ( subfield_is_koha_internal_p($subfield) );
2164                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2165                 my %subfield_data;
2166                 $subfield_data{tag}           = $tag;
2167                 $subfield_data{subfield}      = $subfield;
2168                 $subfield_data{countsubfield} = $cntsubf++;
2169                 $subfield_data{kohafield}     =
2170                   $tagslib->{$tag}->{$subfield}->{'kohafield'};
2171
2172          #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2173                 $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib};
2174                 $subfield_data{mandatory} =
2175                   $tagslib->{$tag}->{$subfield}->{mandatory};
2176                 $subfield_data{repeatable} =
2177                   $tagslib->{$tag}->{$subfield}->{repeatable};
2178                 $subfield_data{hidden} = "display:none"
2179                   if $tagslib->{$tag}->{$subfield}->{hidden};
2180                 my ( $x, $value );
2181                 ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord )
2182                   if ($itemrecord);
2183                 $value =~ s/"/&quot;/g;
2184
2185                 # search for itemcallnumber if applicable
2186                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2187                     'items.itemcallnumber'
2188                     && C4::Context->preference('itemcallnumber') )
2189                 {
2190                     my $CNtag =
2191                       substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2192                     my $CNsubfield =
2193                       substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2194                     my $temp = $itemrecord->field($CNtag) if ($itemrecord);
2195                     if ($temp) {
2196                         $value = $temp->subfield($CNsubfield);
2197                     }
2198                 }
2199                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2200                     'items.itemcallnumber'
2201                     && $defaultvalues->{'callnumber'} )
2202                 {
2203                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2204                     unless ($temp) {
2205                         $value = $defaultvalues->{'callnumber'};
2206                     }
2207                 }
2208                 if ( ($tagslib->{$tag}->{$subfield}->{kohafield} eq
2209                     'items.holdingbranch' ||
2210                     $tagslib->{$tag}->{$subfield}->{kohafield} eq
2211                     'items.homebranch')          
2212                     && $defaultvalues->{'branchcode'} )
2213                 {
2214                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2215                     unless ($temp) {
2216                         $value = $defaultvalues->{branchcode};
2217                     }
2218                 }
2219                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2220                     my @authorised_values;
2221                     my %authorised_lib;
2222
2223                     # builds list, depending on authorised value...
2224                     #---- branch
2225                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
2226                         "branches" )
2227                     {
2228                         if ( ( C4::Context->preference("IndependantBranches") )
2229                             && ( C4::Context->userenv->{flags} % 2 != 1 ) )
2230                         {
2231                             my $sth =
2232                               $dbh->prepare(
2233                                 "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname"
2234                               );
2235                             $sth->execute( C4::Context->userenv->{branch} );
2236                             push @authorised_values, ""
2237                               unless (
2238                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2239                             while ( my ( $branchcode, $branchname ) =
2240                                 $sth->fetchrow_array )
2241                             {
2242                                 push @authorised_values, $branchcode;
2243                                 $authorised_lib{$branchcode} = $branchname;
2244                             }
2245                         }
2246                         else {
2247                             my $sth =
2248                               $dbh->prepare(
2249                                 "SELECT branchcode,branchname FROM branches ORDER BY branchname"
2250                               );
2251                             $sth->execute;
2252                             push @authorised_values, ""
2253                               unless (
2254                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2255                             while ( my ( $branchcode, $branchname ) =
2256                                 $sth->fetchrow_array )
2257                             {
2258                                 push @authorised_values, $branchcode;
2259                                 $authorised_lib{$branchcode} = $branchname;
2260                             }
2261                         }
2262
2263                         #----- itemtypes
2264                     }
2265                     elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
2266                         "itemtypes" )
2267                     {
2268                         my $sth =
2269                           $dbh->prepare(
2270                             "SELECT itemtype,description FROM itemtypes ORDER BY description"
2271                           );
2272                         $sth->execute;
2273                         push @authorised_values, ""
2274                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2275                         while ( my ( $itemtype, $description ) =
2276                             $sth->fetchrow_array )
2277                         {
2278                             push @authorised_values, $itemtype;
2279                             $authorised_lib{$itemtype} = $description;
2280                         }
2281
2282                         #---- "true" authorised value
2283                     }
2284                     else {
2285                         $authorised_values_sth->execute(
2286                             $tagslib->{$tag}->{$subfield}->{authorised_value} );
2287                         push @authorised_values, ""
2288                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2289                         while ( my ( $value, $lib ) =
2290                             $authorised_values_sth->fetchrow_array )
2291                         {
2292                             push @authorised_values, $value;
2293                             $authorised_lib{$value} = $lib;
2294                         }
2295                     }
2296                     $subfield_data{marc_value} = CGI::scrolling_list(
2297                         -name     => 'field_value',
2298                         -values   => \@authorised_values,
2299                         -default  => "$value",
2300                         -labels   => \%authorised_lib,
2301                         -size     => 1,
2302                         -tabindex => '',
2303                         -multiple => 0,
2304                     );
2305                 }
2306                 else {
2307                     $subfield_data{marc_value} =
2308 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=\"50\" maxlength=\"255\" />";
2309                 }
2310                 push( @loop_data, \%subfield_data );
2311             }
2312         }
2313     }
2314     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2315       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2316     return {
2317         'itemtagfield'    => $itemtagfield,
2318         'itemtagsubfield' => $itemtagsubfield,
2319         'itemnumber'      => $itemnumber,
2320         'iteminformation' => \@loop_data
2321     };
2322 }
2323 #"
2324
2325 #
2326 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2327 # at the same time
2328 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2329 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2330 # =head2 ModZebrafiles
2331
2332 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2333
2334 # =cut
2335
2336 # sub ModZebrafiles {
2337
2338 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2339
2340 #     my $op;
2341 #     my $zebradir =
2342 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2343 #     unless ( opendir( DIR, "$zebradir" ) ) {
2344 #         warn "$zebradir not found";
2345 #         return;
2346 #     }
2347 #     closedir DIR;
2348 #     my $filename = $zebradir . $biblionumber;
2349
2350 #     if ($record) {
2351 #         open( OUTPUT, ">", $filename . ".xml" );
2352 #         print OUTPUT $record;
2353 #         close OUTPUT;
2354 #     }
2355 # }
2356
2357 =head2 ModZebra
2358
2359 =over 4
2360
2361 ModZebra( $biblionumber, $op, $server, $oldRecord, $newRecord );
2362
2363     $biblionumber is the biblionumber we want to index
2364     $op is specialUpdate or delete, and is used to know what we want to do
2365     $server is the server that we want to update
2366     $oldRecord is the MARC::Record containing the previous version of the record.  This is used only when 
2367       NoZebra=1, as NoZebra indexing needs to know the previous version of a record in order to
2368       do an update.
2369     $newRecord is the MARC::Record containing the new record. It is usefull only when NoZebra=1, and is used to know what to add to the nozebra database. (the record in mySQL being, if it exist, the previous record, the one just before the modif. We need both : the previous and the new one.
2370     
2371 =back
2372
2373 =cut
2374
2375 sub ModZebra {
2376 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2377     my ( $biblionumber, $op, $server, $oldRecord, $newRecord ) = @_;
2378     my $dbh=C4::Context->dbh;
2379
2380     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2381     # at the same time
2382     # replaced by a zebraqueue table, that is filled with ModZebra to run.
2383     # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2384
2385     if (C4::Context->preference("NoZebra")) {
2386         # lock the nozebra table : we will read index lines, update them in Perl process
2387         # and write everything in 1 transaction.
2388         # lock the table to avoid someone else overwriting what we are doing
2389         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE, auth_subfield_structure READ');
2390         my %result; # the result hash that will be built by deletion / add, and written on mySQL at the end, to improve speed
2391         if ($op eq 'specialUpdate') {
2392             # OK, we have to add or update the record
2393             # 1st delete (virtually, in indexes), if record actually exists
2394             if ($oldRecord) { 
2395                 %result = _DelBiblioNoZebra($biblionumber,$oldRecord,$server);
2396             }
2397             # ... add the record
2398             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
2399         } else {
2400             # it's a deletion, delete the record...
2401             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2402             %result=_DelBiblioNoZebra($biblionumber,$oldRecord,$server);
2403         }
2404         # ok, now update the database...
2405         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2406         foreach my $key (keys %result) {
2407             foreach my $index (keys %{$result{$key}}) {
2408                 $sth->execute($result{$key}->{$index}, $server, $key, $index);
2409             }
2410         }
2411         $dbh->do('UNLOCK TABLES');
2412     } else {
2413         #
2414         # we use zebra, just fill zebraqueue table
2415         #
2416         my $check_sql = "SELECT COUNT(*) FROM zebraqueue 
2417                          WHERE server = ?
2418                          AND   biblio_auth_number = ?
2419                          AND   operation = ?
2420                          AND   done = 0";
2421         my $check_sth = $dbh->prepare_cached($check_sql);
2422         $check_sth->execute($server, $biblionumber, $op);
2423         my ($count) = $check_sth->fetchrow_array;
2424         $check_sth->finish();
2425         if ($count == 0) {
2426             my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2427             $sth->execute($biblionumber,$server,$op);
2428             $sth->finish;
2429         }
2430     }
2431 }
2432
2433 =head2 GetNoZebraIndexes
2434
2435     %indexes = GetNoZebraIndexes;
2436     
2437     return the data from NoZebraIndexes syspref.
2438
2439 =cut
2440
2441 sub GetNoZebraIndexes {
2442     my $no_zebra_indexes = C4::Context->preference('NoZebraIndexes');
2443     my %indexes;
2444     INDEX: foreach my $line (split /['"],[\n\r]*/,$no_zebra_indexes) {
2445         $line =~ /(.*)=>(.*)/;
2446         my $index = $1; # initial ' or " is removed afterwards
2447         my $fields = $2;
2448         $index =~ s/'|"|\s//g;
2449         $fields =~ s/'|"|\s//g;
2450         $indexes{$index}=$fields;
2451     }
2452     return %indexes;
2453 }
2454
2455 =head1 INTERNAL FUNCTIONS
2456
2457 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2458
2459     function to delete a biblio in NoZebra indexes
2460     This function does NOT delete anything in database : it reads all the indexes entries
2461     that have to be deleted & delete them in the hash
2462     The SQL part is done either :
2463     - after the Add if we are modifying a biblio (delete + add again)
2464     - immediatly after this sub if we are doing a true deletion.
2465     $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2466
2467 =cut
2468
2469
2470 sub _DelBiblioNoZebra {
2471     my ($biblionumber, $record, $server)=@_;
2472     
2473     # Get the indexes
2474     my $dbh = C4::Context->dbh;
2475     # Get the indexes
2476     my %index;
2477     my $title;
2478     if ($server eq 'biblioserver') {
2479         %index=GetNoZebraIndexes;
2480         # get title of the record (to store the 10 first letters with the index)
2481         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title', ''); # FIXME: should be GetFrameworkCode($biblionumber) ??
2482         $title = lc($record->subfield($titletag,$titlesubfield));
2483     } else {
2484         # for authorities, the "title" is the $a mainentry
2485         my ($auth_type_tag, $auth_type_sf) = C4::AuthoritiesMarc::get_auth_type_location();
2486         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield($auth_type_tag, $auth_type_sf));
2487         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2488         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2489         $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
2490         $index{'mainentry'}    = $authref->{'auth_tag_to_report'}.'*';
2491         $index{'auth_type'}    = "${auth_type_tag}${auth_type_sf}";
2492     }
2493     
2494     my %result;
2495     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2496     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2497     # limit to 10 char, should be enough, and limit the DB size
2498     $title = substr($title,0,10);
2499     #parse each field
2500     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2501     foreach my $field ($record->fields()) {
2502         #parse each subfield
2503         next if $field->tag <10;
2504         foreach my $subfield ($field->subfields()) {
2505             my $tag = $field->tag();
2506             my $subfieldcode = $subfield->[0];
2507             my $indexed=0;
2508             # check each index to see if the subfield is stored somewhere
2509             # otherwise, store it in __RAW__ index
2510             foreach my $key (keys %index) {
2511 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2512                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2513                     $indexed=1;
2514                     my $line= lc $subfield->[1];
2515                     # remove meaningless value in the field...
2516                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2517                     # ... and split in words
2518                     foreach (split / /,$line) {
2519                         next unless $_; # skip  empty values (multiple spaces)
2520                         # if the entry is already here, do nothing, the biblionumber has already be removed
2521                         unless ( defined( $result{$key}->{$_} ) && ( $result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) ) {
2522                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2523                             $sth2->execute($server,$key,$_);
2524                             my $existing_biblionumbers = $sth2->fetchrow;
2525                             # it exists
2526                             if ($existing_biblionumbers) {
2527 #                                 warn " existing for $key $_: $existing_biblionumbers";
2528                                 $result{$key}->{$_} =$existing_biblionumbers;
2529                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2530                             }
2531                         }
2532                     }
2533                 }
2534             }
2535             # the subfield is not indexed, store it in __RAW__ index anyway
2536             unless ($indexed) {
2537                 my $line= lc $subfield->[1];
2538                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2539                 # ... and split in words
2540                 foreach (split / /,$line) {
2541                     next unless $_; # skip  empty values (multiple spaces)
2542                     # if the entry is already here, do nothing, the biblionumber has already be removed
2543                     unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2544                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2545                         $sth2->execute($server,'__RAW__',$_);
2546                         my $existing_biblionumbers = $sth2->fetchrow;
2547                         # it exists
2548                         if ($existing_biblionumbers) {
2549                             $result{'__RAW__'}->{$_} =$existing_biblionumbers;
2550                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2551                         }
2552                     }
2553                 }
2554             }
2555         }
2556     }
2557     return %result;
2558 }
2559
2560 =head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
2561
2562     function to add a biblio in NoZebra indexes
2563
2564 =cut
2565
2566 sub _AddBiblioNoZebra {
2567     my ($biblionumber, $record, $server, %result)=@_;
2568     my $dbh = C4::Context->dbh;
2569     # Get the indexes
2570     my %index;
2571     my $title;
2572     if ($server eq 'biblioserver') {
2573         %index=GetNoZebraIndexes;
2574         # get title of the record (to store the 10 first letters with the index)
2575         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title', ''); # FIXME: should be GetFrameworkCode($biblionumber) ??
2576         $title = lc($record->subfield($titletag,$titlesubfield));
2577     } else {
2578         # warn "server : $server";
2579         # for authorities, the "title" is the $a mainentry
2580         my ($auth_type_tag, $auth_type_sf) = C4::AuthoritiesMarc::get_auth_type_location();
2581         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield($auth_type_tag, $auth_type_sf));
2582         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2583         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2584         $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
2585         $index{'mainentry'}     = $authref->{auth_tag_to_report}.'*';
2586         $index{'auth_type'}    = "${auth_type_tag}${auth_type_sf}";
2587     }
2588
2589     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2590     $title =~ s/ |\.|,|;|\[|\]|\(|\)|\*|-|'|:|=|\r|\n//g;
2591     # limit to 10 char, should be enough, and limit the DB size
2592     $title = substr($title,0,10);
2593     #parse each field
2594     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2595     foreach my $field ($record->fields()) {
2596         #parse each subfield
2597         ###FIXME: impossible to index a 001-009 value with NoZebra
2598         next if $field->tag <10;
2599         foreach my $subfield ($field->subfields()) {
2600             my $tag = $field->tag();
2601             my $subfieldcode = $subfield->[0];
2602             my $indexed=0;
2603 #             warn "INDEXING :".$subfield->[1];
2604             # check each index to see if the subfield is stored somewhere
2605             # otherwise, store it in __RAW__ index
2606             foreach my $key (keys %index) {
2607 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2608                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2609                     $indexed=1;
2610                     my $line= lc $subfield->[1];
2611                     # remove meaningless value in the field...
2612                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2613                     # ... and split in words
2614                     foreach (split / /,$line) {
2615                         next unless $_; # skip  empty values (multiple spaces)
2616                         # if the entry is already here, improve weight
2617 #                         warn "managing $_";
2618                         if ( exists $result{$key}->{$_} && $result{$key}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d+);/) {
2619                             my $weight = $1 + 1;
2620                             $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2621                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2622                         } else {
2623                             # get the value if it exist in the nozebra table, otherwise, create it
2624                             $sth2->execute($server,$key,$_);
2625                             my $existing_biblionumbers = $sth2->fetchrow;
2626                             # it exists
2627                             if ($existing_biblionumbers) {
2628                                 $result{$key}->{"$_"} =$existing_biblionumbers;
2629                                 my $weight = defined $1 ? $1 + 1 : 1;
2630                                 $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2631                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2632                             # create a new ligne for this entry
2633                             } else {
2634 #                             warn "INSERT : $server / $key / $_";
2635                                 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
2636                                 $result{$key}->{"$_"}.="$biblionumber,$title-1;";
2637                             }
2638                         }
2639                     }
2640                 }
2641             }
2642             # the subfield is not indexed, store it in __RAW__ index anyway
2643             unless ($indexed) {
2644                 my $line= lc $subfield->[1];
2645                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2646                 # ... and split in words
2647                 foreach (split / /,$line) {
2648                     next unless $_; # skip  empty values (multiple spaces)
2649                     # if the entry is already here, improve weight
2650                     if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d+);/) { 
2651                         my $weight=$1+1;
2652                         $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2653                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2654                     } else {
2655                         # get the value if it exist in the nozebra table, otherwise, create it
2656                         $sth2->execute($server,'__RAW__',$_);
2657                         my $existing_biblionumbers = $sth2->fetchrow;
2658                         # it exists
2659                         if ($existing_biblionumbers) {
2660                             $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
2661                             my $weight=$1+1;
2662                             $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2663                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2664                         # create a new ligne for this entry
2665                         } else {
2666                             $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).',  indexname="__RAW__",value='.$dbh->quote($_));
2667                             $result{'__RAW__'}->{"$_"}.="$biblionumber,$title-1;";
2668                         }
2669                     }
2670                 }
2671             }
2672         }
2673     }
2674     return %result;
2675 }
2676
2677
2678 =head2 _find_value
2679
2680 =over 4
2681
2682 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2683
2684 Find the given $subfield in the given $tag in the given
2685 MARC::Record $record.  If the subfield is found, returns
2686 the (indicators, value) pair; otherwise, (undef, undef) is
2687 returned.
2688
2689 PROPOSITION :
2690 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2691 I suggest we export it from this module.
2692
2693 =back
2694
2695 =cut
2696
2697 sub _find_value {
2698     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2699     my @result;
2700     my $indicator;
2701     if ( $tagfield < 10 ) {
2702         if ( $record->field($tagfield) ) {
2703             push @result, $record->field($tagfield)->data();
2704         }
2705         else {
2706             push @result, "";
2707         }
2708     }
2709     else {
2710         foreach my $field ( $record->field($tagfield) ) {
2711             my @subfields = $field->subfields();
2712             foreach my $subfield (@subfields) {
2713                 if ( @$subfield[0] eq $insubfield ) {
2714                     push @result, @$subfield[1];
2715                     $indicator = $field->indicator(1) . $field->indicator(2);
2716                 }
2717             }
2718         }
2719     }
2720     return ( $indicator, @result );
2721 }
2722
2723 =head2 _koha_marc_update_bib_ids
2724
2725 =over 4
2726
2727 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
2728
2729 Internal function to add or update biblionumber and biblioitemnumber to
2730 the MARC XML.
2731
2732 =back
2733
2734 =cut
2735
2736 sub _koha_marc_update_bib_ids {
2737     my ($record, $frameworkcode, $biblionumber, $biblioitemnumber) = @_;
2738
2739     # we must add bibnum and bibitemnum in MARC::Record...
2740     # we build the new field with biblionumber and biblioitemnumber
2741     # we drop the original field
2742     # we add the new builded field.
2743     my ($biblio_tag, $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
2744     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
2745
2746     if ($biblio_tag != $biblioitem_tag) {
2747         # biblionumber & biblioitemnumber are in different fields
2748
2749         # deal with biblionumber
2750         my ($new_field, $old_field);
2751         if ($biblio_tag < 10) {
2752             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
2753         } else {
2754             $new_field =
2755               MARC::Field->new( $biblio_tag, '', '',
2756                 "$biblio_subfield" => $biblionumber );
2757         }
2758
2759         # drop old field and create new one...
2760         $old_field = $record->field($biblio_tag);
2761         $record->delete_field($old_field) if $old_field;
2762         $record->append_fields($new_field);
2763
2764         # deal with biblioitemnumber
2765         if ($biblioitem_tag < 10) {
2766             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
2767         } else {
2768             $new_field =
2769               MARC::Field->new( $biblioitem_tag, '', '',
2770                 "$biblioitem_subfield" => $biblioitemnumber, );
2771         }
2772         # drop old field and create new one...
2773         $old_field = $record->field($biblioitem_tag);
2774         $record->delete_field($old_field) if $old_field;
2775         $record->insert_fields_ordered($new_field);
2776
2777     } else {
2778         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
2779         my $new_field = MARC::Field->new(
2780             $biblio_tag, '', '',
2781             "$biblio_subfield" => $biblionumber,
2782             "$biblioitem_subfield" => $biblioitemnumber
2783         );
2784
2785         # drop old field and create new one...
2786         my $old_field = $record->field($biblio_tag);
2787         $record->delete_field($old_field) if $old_field;
2788         $record->insert_fields_ordered($new_field);
2789     }
2790 }
2791
2792 =head2 _koha_marc_update_biblioitem_cn_sort
2793
2794 =over 4
2795
2796 _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
2797
2798 =back
2799
2800 Given a MARC bib record and the biblioitem hash, update the
2801 subfield that contains a copy of the value of biblioitems.cn_sort.
2802
2803 =cut
2804
2805 sub _koha_marc_update_biblioitem_cn_sort {
2806     my $marc = shift;
2807     my $biblioitem = shift;
2808     my $frameworkcode= shift;
2809
2810     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.cn_sort",$frameworkcode);
2811     return unless $biblioitem_tag;
2812
2813     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2814
2815     if (my $field = $marc->field($biblioitem_tag)) {
2816         $field->delete_subfield(code => $biblioitem_subfield);
2817         if ($cn_sort ne '') {
2818             $field->add_subfields($biblioitem_subfield => $cn_sort);
2819         }
2820     } else {
2821         # if we get here, no biblioitem tag is present in the MARC record, so
2822         # we'll create it if $cn_sort is not empty -- this would be
2823         # an odd combination of events, however
2824         if ($cn_sort) {
2825             $marc->insert_grouped_field(MARC::Field->new($biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort));
2826         }
2827     }
2828 }
2829
2830 =head2 _koha_add_biblio
2831
2832 =over 4
2833
2834 my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
2835
2836 Internal function to add a biblio ($biblio is a hash with the values)
2837
2838 =back
2839
2840 =cut
2841
2842 sub _koha_add_biblio {
2843     my ( $dbh, $biblio, $frameworkcode ) = @_;
2844
2845     my $error;
2846
2847     # set the series flag
2848     my $serial = 0;
2849     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
2850
2851     my $query = 
2852         "INSERT INTO biblio
2853         SET frameworkcode = ?,
2854             author = ?,
2855             title = ?,
2856             unititle =?,
2857             notes = ?,
2858             serial = ?,
2859             seriestitle = ?,
2860             copyrightdate = ?,
2861             datecreated=NOW(),
2862             abstract = ?
2863         ";
2864     my $sth = $dbh->prepare($query);
2865     $sth->execute(
2866         $frameworkcode,
2867         $biblio->{'author'},
2868         $biblio->{'title'},
2869         $biblio->{'unititle'},
2870         $biblio->{'notes'},
2871         $serial,
2872         $biblio->{'seriestitle'},
2873         $biblio->{'copyrightdate'},
2874         $biblio->{'abstract'}
2875     );
2876
2877     my $biblionumber = $dbh->{'mysql_insertid'};
2878     if ( $dbh->errstr ) {
2879         $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
2880         warn $error;
2881     }
2882
2883     $sth->finish();
2884     #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
2885     return ($biblionumber,$error);
2886 }
2887
2888 =head2 _koha_modify_biblio
2889
2890 =over 4
2891
2892 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
2893
2894 Internal function for updating the biblio table
2895
2896 =back
2897
2898 =cut
2899
2900 sub _koha_modify_biblio {
2901     my ( $dbh, $biblio, $frameworkcode ) = @_;
2902     my $error;
2903
2904     my $query = "
2905         UPDATE biblio
2906         SET    frameworkcode = ?,
2907                author = ?,
2908                title = ?,
2909                unititle = ?,
2910                notes = ?,
2911                serial = ?,
2912                seriestitle = ?,
2913                copyrightdate = ?,
2914                abstract = ?
2915         WHERE  biblionumber = ?
2916         "
2917     ;
2918     my $sth = $dbh->prepare($query);
2919     
2920     $sth->execute(
2921         $frameworkcode,
2922         $biblio->{'author'},
2923         $biblio->{'title'},
2924         $biblio->{'unititle'},
2925         $biblio->{'notes'},
2926         $biblio->{'serial'},
2927         $biblio->{'seriestitle'},
2928         $biblio->{'copyrightdate'},
2929         $biblio->{'abstract'},
2930         $biblio->{'biblionumber'}
2931     ) if $biblio->{'biblionumber'};
2932
2933     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
2934         $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
2935         warn $error;
2936     }
2937     return ( $biblio->{'biblionumber'},$error );
2938 }
2939
2940 =head2 _koha_modify_biblioitem_nonmarc
2941
2942 =over 4
2943
2944 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
2945
2946 Updates biblioitems row except for marc and marcxml, which should be changed
2947 via ModBiblioMarc
2948
2949 =back
2950
2951 =cut
2952
2953 sub _koha_modify_biblioitem_nonmarc {
2954     my ( $dbh, $biblioitem ) = @_;
2955     my $error;
2956
2957     # re-calculate the cn_sort, it may have changed
2958     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2959
2960     my $query = 
2961     "UPDATE biblioitems 
2962     SET biblionumber    = ?,
2963         volume          = ?,
2964         number          = ?,
2965         itemtype        = ?,
2966         isbn            = ?,
2967         issn            = ?,
2968         publicationyear = ?,
2969         publishercode   = ?,
2970         volumedate      = ?,
2971         volumedesc      = ?,
2972         collectiontitle = ?,
2973         collectionissn  = ?,
2974         collectionvolume= ?,
2975         editionstatement= ?,
2976         editionresponsibility = ?,
2977         illus           = ?,
2978         pages           = ?,
2979         notes           = ?,
2980         size            = ?,
2981         place           = ?,
2982         lccn            = ?,
2983         url             = ?,
2984         cn_source       = ?,
2985         cn_class        = ?,
2986         cn_item         = ?,
2987         cn_suffix       = ?,
2988         cn_sort         = ?,
2989         totalissues     = ?
2990         where biblioitemnumber = ?
2991         ";
2992     my $sth = $dbh->prepare($query);
2993     $sth->execute(
2994         $biblioitem->{'biblionumber'},
2995         $biblioitem->{'volume'},
2996         $biblioitem->{'number'},
2997         $biblioitem->{'itemtype'},
2998         $biblioitem->{'isbn'},
2999         $biblioitem->{'issn'},
3000         $biblioitem->{'publicationyear'},
3001         $biblioitem->{'publishercode'},
3002         $biblioitem->{'volumedate'},
3003         $biblioitem->{'volumedesc'},
3004         $biblioitem->{'collectiontitle'},
3005         $biblioitem->{'collectionissn'},
3006         $biblioitem->{'collectionvolume'},
3007         $biblioitem->{'editionstatement'},
3008         $biblioitem->{'editionresponsibility'},
3009         $biblioitem->{'illus'},
3010         $biblioitem->{'pages'},
3011         $biblioitem->{'bnotes'},
3012         $biblioitem->{'size'},
3013         $biblioitem->{'place'},
3014         $biblioitem->{'lccn'},
3015         $biblioitem->{'url'},
3016         $biblioitem->{'biblioitems.cn_source'},
3017         $biblioitem->{'cn_class'},
3018         $biblioitem->{'cn_item'},
3019         $biblioitem->{'cn_suffix'},
3020         $cn_sort,
3021         $biblioitem->{'totalissues'},
3022         $biblioitem->{'biblioitemnumber'}
3023     );
3024     if ( $dbh->errstr ) {
3025         $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
3026         warn $error;
3027     }
3028     return ($biblioitem->{'biblioitemnumber'},$error);
3029 }
3030
3031 =head2 _koha_add_biblioitem
3032
3033 =over 4
3034
3035 my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3036
3037 Internal function to add a biblioitem
3038
3039 =back
3040
3041 =cut
3042
3043 sub _koha_add_biblioitem {
3044     my ( $dbh, $biblioitem ) = @_;
3045     my $error;
3046
3047     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3048     my $query =
3049     "INSERT INTO biblioitems SET
3050         biblionumber    = ?,
3051         volume          = ?,
3052         number          = ?,
3053         itemtype        = ?,
3054         isbn            = ?,
3055         issn            = ?,
3056         publicationyear = ?,
3057         publishercode   = ?,
3058         volumedate      = ?,
3059         volumedesc      = ?,
3060         collectiontitle = ?,
3061         collectionissn  = ?,
3062         collectionvolume= ?,
3063         editionstatement= ?,
3064         editionresponsibility = ?,
3065         illus           = ?,
3066         pages           = ?,
3067         notes           = ?,
3068         size            = ?,
3069         place           = ?,
3070         lccn            = ?,
3071         marc            = ?,
3072         url             = ?,
3073         cn_source       = ?,
3074         cn_class        = ?,
3075         cn_item         = ?,
3076         cn_suffix       = ?,
3077         cn_sort         = ?,
3078         totalissues     = ?
3079         ";
3080     my $sth = $dbh->prepare($query);
3081     $sth->execute(
3082         $biblioitem->{'biblionumber'},
3083         $biblioitem->{'volume'},
3084         $biblioitem->{'number'},
3085         $biblioitem->{'itemtype'},
3086         $biblioitem->{'isbn'},
3087         $biblioitem->{'issn'},
3088         $biblioitem->{'publicationyear'},
3089         $biblioitem->{'publishercode'},
3090         $biblioitem->{'volumedate'},
3091         $biblioitem->{'volumedesc'},
3092         $biblioitem->{'collectiontitle'},
3093         $biblioitem->{'collectionissn'},
3094         $biblioitem->{'collectionvolume'},
3095         $biblioitem->{'editionstatement'},
3096         $biblioitem->{'editionresponsibility'},
3097         $biblioitem->{'illus'},
3098         $biblioitem->{'pages'},
3099         $biblioitem->{'bnotes'},
3100         $biblioitem->{'size'},
3101         $biblioitem->{'place'},
3102         $biblioitem->{'lccn'},
3103         $biblioitem->{'marc'},
3104         $biblioitem->{'url'},
3105         $biblioitem->{'biblioitems.cn_source'},
3106         $biblioitem->{'cn_class'},
3107         $biblioitem->{'cn_item'},
3108         $biblioitem->{'cn_suffix'},
3109         $cn_sort,
3110         $biblioitem->{'totalissues'}
3111     );
3112     my $bibitemnum = $dbh->{'mysql_insertid'};
3113     if ( $dbh->errstr ) {
3114         $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
3115         warn $error;
3116     }
3117     $sth->finish();
3118     return ($bibitemnum,$error);
3119 }
3120
3121 =head2 _koha_delete_biblio
3122
3123 =over 4
3124
3125 $error = _koha_delete_biblio($dbh,$biblionumber);
3126
3127 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3128
3129 C<$dbh> - the database handle
3130 C<$biblionumber> - the biblionumber of the biblio to be deleted
3131
3132 =back
3133
3134 =cut
3135
3136 # FIXME: add error handling
3137
3138 sub _koha_delete_biblio {
3139     my ( $dbh, $biblionumber ) = @_;
3140
3141     # get all the data for this biblio
3142     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3143     $sth->execute($biblionumber);
3144
3145     if ( my $data = $sth->fetchrow_hashref ) {
3146
3147         # save the record in deletedbiblio
3148         # find the fields to save
3149         my $query = "INSERT INTO deletedbiblio SET ";
3150         my @bind  = ();
3151         foreach my $temp ( keys %$data ) {
3152             $query .= "$temp = ?,";
3153             push( @bind, $data->{$temp} );
3154         }
3155
3156         # replace the last , by ",?)"
3157         $query =~ s/\,$//;
3158         my $bkup_sth = $dbh->prepare($query);
3159         $bkup_sth->execute(@bind);
3160         $bkup_sth->finish;
3161
3162         # delete the biblio
3163         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3164         $del_sth->execute($biblionumber);
3165         $del_sth->finish;
3166     }
3167     $sth->finish;
3168     return undef;
3169 }
3170
3171 =head2 _koha_delete_biblioitems
3172
3173 =over 4
3174
3175 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3176
3177 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3178
3179 C<$dbh> - the database handle
3180 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3181
3182 =back
3183
3184 =cut
3185
3186 # FIXME: add error handling
3187
3188 sub _koha_delete_biblioitems {
3189     my ( $dbh, $biblioitemnumber ) = @_;
3190
3191     # get all the data for this biblioitem
3192     my $sth =
3193       $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3194     $sth->execute($biblioitemnumber);
3195
3196     if ( my $data = $sth->fetchrow_hashref ) {
3197
3198         # save the record in deletedbiblioitems
3199         # find the fields to save
3200         my $query = "INSERT INTO deletedbiblioitems SET ";
3201         my @bind  = ();
3202         foreach my $temp ( keys %$data ) {
3203             $query .= "$temp = ?,";
3204             push( @bind, $data->{$temp} );
3205         }
3206
3207         # replace the last , by ",?)"
3208         $query =~ s/\,$//;
3209         my $bkup_sth = $dbh->prepare($query);
3210         $bkup_sth->execute(@bind);
3211         $bkup_sth->finish;
3212
3213         # delete the biblioitem
3214         my $del_sth =
3215           $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3216         $del_sth->execute($biblioitemnumber);
3217         $del_sth->finish;
3218     }
3219     $sth->finish;
3220     return undef;
3221 }
3222
3223 =head1 UNEXPORTED FUNCTIONS
3224
3225 =head2 ModBiblioMarc
3226
3227     &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3228     
3229     Add MARC data for a biblio to koha 
3230     
3231     Function exported, but should NOT be used, unless you really know what you're doing
3232
3233 =cut
3234
3235 sub ModBiblioMarc {
3236     
3237 # pass the MARC::Record to this function, and it will create the records in the marc field
3238     my ( $record, $biblionumber, $frameworkcode ) = @_;
3239     my $dbh = C4::Context->dbh;
3240     my @fields = $record->fields();
3241     if ( !$frameworkcode ) {
3242         $frameworkcode = "";
3243     }
3244     my $sth =
3245       $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3246     $sth->execute( $frameworkcode, $biblionumber );
3247     $sth->finish;
3248     my $encoding = C4::Context->preference("marcflavour");
3249
3250     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3251     if ( $encoding eq "UNIMARC" ) {
3252         my $string;
3253         if ( length($record->subfield( 100, "a" )) == 35 ) {
3254             $string = $record->subfield( 100, "a" );
3255             my $f100 = $record->field(100);
3256             $record->delete_field($f100);
3257         }
3258         else {
3259             $string = POSIX::strftime( "%Y%m%d", localtime );
3260             $string =~ s/\-//g;
3261             $string = sprintf( "%-*s", 35, $string );
3262         }
3263         substr( $string, 22, 6, "frey50" );
3264         unless ( $record->subfield( 100, "a" ) ) {
3265             $record->insert_grouped_field(
3266                 MARC::Field->new( 100, "", "", "a" => $string ) );
3267         }
3268     }
3269     my $oldRecord;
3270     if (C4::Context->preference("NoZebra")) {
3271         # only NoZebra indexing needs to have
3272         # the previous version of the record
3273         $oldRecord = GetMarcBiblio($biblionumber);
3274     }
3275     $sth =
3276       $dbh->prepare(
3277         "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
3278     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
3279         $biblionumber );
3280     $sth->finish;
3281     ModZebra($biblionumber,"specialUpdate","biblioserver",$oldRecord,$record);
3282     return $biblionumber;
3283 }
3284
3285 =head2 z3950_extended_services
3286
3287 z3950_extended_services($serviceType,$serviceOptions,$record);
3288
3289     z3950_extended_services is used to handle all interactions with Zebra's extended serices package, which is employed to perform all management of the MARC data stored in Zebra.
3290
3291 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
3292
3293 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
3294
3295     action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
3296
3297 and maybe
3298
3299     recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
3300     syntax => the record syntax (transfer syntax)
3301     databaseName = Database from connection object
3302
3303     To set serviceOptions, call set_service_options($serviceType)
3304
3305 C<$record> the record, if one is needed for the service type
3306
3307     A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
3308
3309 =cut
3310
3311 sub z3950_extended_services {
3312     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
3313
3314     # get our connection object
3315     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
3316
3317     # create a new package object
3318     my $Zpackage = $Zconn->package();
3319
3320     # set our options
3321     $Zpackage->option( action => $action );
3322
3323     if ( $serviceOptions->{'databaseName'} ) {
3324         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
3325     }
3326     if ( $serviceOptions->{'recordIdNumber'} ) {
3327         $Zpackage->option(
3328             recordIdNumber => $serviceOptions->{'recordIdNumber'} );
3329     }
3330     if ( $serviceOptions->{'recordIdOpaque'} ) {
3331         $Zpackage->option(
3332             recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
3333     }
3334
3335  # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
3336  #if ($serviceType eq 'itemorder') {
3337  #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
3338  #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
3339  #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
3340  #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
3341  #}
3342
3343     if ( $serviceOptions->{record} ) {
3344         $Zpackage->option( record => $serviceOptions->{record} );
3345
3346         # can be xml or marc
3347         if ( $serviceOptions->{'syntax'} ) {
3348             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
3349         }
3350     }
3351
3352     # send the request, handle any exception encountered
3353     eval { $Zpackage->send($serviceType) };
3354     if ( $@ && $@->isa("ZOOM::Exception") ) {
3355         return "error:  " . $@->code() . " " . $@->message() . "\n";
3356     }
3357
3358     # free up package resources
3359     $Zpackage->destroy();
3360 }
3361
3362 =head2 set_service_options
3363
3364 my $serviceOptions = set_service_options($serviceType);
3365
3366 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
3367
3368 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
3369
3370 =cut
3371
3372 sub set_service_options {
3373     my ($serviceType) = @_;
3374     my $serviceOptions;
3375
3376 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
3377 #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
3378
3379     if ( $serviceType eq 'commit' ) {
3380
3381         # nothing to do
3382     }
3383     if ( $serviceType eq 'create' ) {
3384
3385         # nothing to do
3386     }
3387     if ( $serviceType eq 'drop' ) {
3388         die "ERROR: 'drop' not currently supported (by Zebra)";
3389     }
3390     return $serviceOptions;
3391 }
3392
3393 =head3 get_biblio_authorised_values
3394
3395   find the types and values for all authorised values assigned to this biblio.
3396
3397   parameters:
3398     biblionumber
3399     MARC::Record of the bib
3400
3401   returns: a hashref malling the authorised value to the value set for this biblionumber
3402
3403       $authorised_values = {
3404                              'Scent'     => 'flowery',
3405                              'Audience'  => 'Young Adult',
3406                              'itemtypes' => 'SER',
3407                            };
3408
3409   Notes: forlibrarian should probably be passed in, and called something different.
3410
3411
3412 =cut
3413
3414 sub get_biblio_authorised_values {
3415     my $biblionumber = shift;
3416     my $record       = shift;
3417     
3418     my $forlibrarian = 1; # are we in staff or opac?
3419     my $frameworkcode = GetFrameworkCode( $biblionumber );
3420
3421     my $authorised_values;
3422
3423     my $tagslib = GetMarcStructure( $forlibrarian, $frameworkcode )
3424       or return $authorised_values;
3425
3426     # assume that these entries in the authorised_value table are bibliolevel.
3427     # ones that start with 'item%' are item level.
3428     my $query = q(SELECT distinct authorised_value, kohafield
3429                     FROM marc_subfield_structure
3430                     WHERE authorised_value !=''
3431                       AND (kohafield like 'biblio%'
3432                        OR  kohafield like '') );
3433     my $bibliolevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
3434     
3435     foreach my $tag ( keys( %$tagslib ) ) {
3436         foreach my $subfield ( keys( %{$tagslib->{ $tag }} ) ) {
3437             # warn "checking $subfield. type is: " . ref $tagslib->{ $tag }{ $subfield };
3438             if ( 'HASH' eq ref $tagslib->{ $tag }{ $subfield } ) {
3439                 if ( defined $tagslib->{ $tag }{ $subfield }{'authorised_value'} && exists $bibliolevel_authorised_values->{ $tagslib->{ $tag }{ $subfield }{'authorised_value'} } ) {
3440                     if ( defined $record->field( $tag ) ) {
3441                         my $this_subfield_value = $record->field( $tag )->subfield( $subfield );
3442                         if ( defined $this_subfield_value ) {
3443                             $authorised_values->{ $tagslib->{ $tag }{ $subfield }{'authorised_value'} } = $this_subfield_value;
3444                         }
3445                     }
3446                 }
3447             }
3448         }
3449     }
3450     # warn ( Data::Dumper->Dump( [ $authorised_values ], [ 'authorised_values' ] ) );
3451     return $authorised_values;
3452 }
3453
3454
3455 1;
3456
3457 __END__
3458
3459 =head1 AUTHOR
3460
3461 Koha Developement team <info@koha.org>
3462
3463 Paul POULAIN paul.poulain@free.fr
3464
3465 Joshua Ferraro jmf@liblime.com
3466
3467 =cut