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