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