a0c325a19c9a85f269de8d8d332e9ac16fb70ad9
[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 $record          = GetMarcBiblio($biblionumber);
770     my $itemtype        = &GetFrameworkCode($biblionumber);
771     my ($holdingbrtagf,$holdingbrtagsubf) = &GetMarcFromKohaField("items.holdingbranch",$itemtype);
772     my $tagslib      = &GetMarcStructure( 1, $itemtype );
773     
774     my $ISBD = C4::Context->preference('ISBD');
775     my $bloc = $ISBD;
776     my $res;
777     my $blocres;
778     
779     foreach my $isbdfield ( split (/#/, $bloc) ) {
780
781         #         $isbdfield= /(.?.?.?)/;
782         $isbdfield =~ /(\d\d\d)([^\|])?\|(.*)\|(.*)\|(.*)/;
783         my $fieldvalue    = $1 || 0;
784         my $subfvalue     = $2 || "";
785         my $textbefore    = $3;
786         my $analysestring = $4;
787         my $textafter     = $5;
788     
789         #         warn "==> $1 / $2 / $3 / $4";
790         #         my $fieldvalue=substr($isbdfield,0,3);
791         if ( $fieldvalue > 0 ) {
792             my $hasputtextbefore = 0;
793             my @fieldslist = $record->field($fieldvalue);
794             @fieldslist = sort {$a->subfield($holdingbrtagsubf) cmp $b->subfield($holdingbrtagsubf)} @fieldslist if ($fieldvalue eq $holdingbrtagf);
795     
796             #         warn "ERROR IN ISBD DEFINITION at : $isbdfield" unless $fieldvalue;
797             #             warn "FV : $fieldvalue";
798             if ($subfvalue ne ""){
799               foreach my $field ( @fieldslist ) {
800                 foreach my $subfield ($field->subfield($subfvalue)){ 
801                   my $calculated = $analysestring;
802                   my $tag        = $field->tag();
803                   if ( $tag < 10 ) {
804                   }
805                   else {
806                     my $subfieldvalue =
807                     GetAuthorisedValueDesc( $tag, $subfvalue,
808                       $subfield, '', $tagslib );
809                     my $tagsubf = $tag . $subfvalue;
810                     $calculated =~
811                           s/\{(.?.?.?.?)$tagsubf(.*?)\}/$1$subfieldvalue$2\{$1$tagsubf$2\}/g;
812                     $calculated =~s#/cgi-bin/koha/[^/]+/([^.]*.pl\?.*)$#opac-$1#g;
813                 
814                     # field builded, store the result
815                     if ( $calculated && !$hasputtextbefore )
816                     {    # put textbefore if not done
817                     $blocres .= $textbefore;
818                     $hasputtextbefore = 1;
819                     }
820                 
821                     # remove punctuation at start
822                     $calculated =~ s/^( |;|:|\.|-)*//g;
823                     $blocres .= $calculated;
824                                 
825                   }
826                 }
827               }
828               $blocres .= $textafter if $hasputtextbefore;
829             } else {    
830             foreach my $field ( @fieldslist ) {
831               my $calculated = $analysestring;
832               my $tag        = $field->tag();
833               if ( $tag < 10 ) {
834               }
835               else {
836                 my @subf = $field->subfields;
837                 for my $i ( 0 .. $#subf ) {
838                 my $valuecode   = $subf[$i][1];
839                 my $subfieldcode  = $subf[$i][0];
840                 my $subfieldvalue =
841                 GetAuthorisedValueDesc( $tag, $subf[$i][0],
842                   $subf[$i][1], '', $tagslib );
843                 my $tagsubf = $tag . $subfieldcode;
844     
845                 $calculated =~ s/                  # replace all {{}} codes by the value code.
846                                   \{\{$tagsubf\}\} # catch the {{actualcode}}
847                                 /
848                                   $valuecode     # replace by the value code
849                                /gx;
850     
851                 $calculated =~
852             s/\{(.?.?.?.?)$tagsubf(.*?)\}/$1$subfieldvalue$2\{$1$tagsubf$2\}/g;
853             $calculated =~s#/cgi-bin/koha/[^/]+/([^.]*.pl\?.*)$#opac-$1#g;
854                 }
855     
856                 # field builded, store the result
857                 if ( $calculated && !$hasputtextbefore )
858                 {    # put textbefore if not done
859                 $blocres .= $textbefore;
860                 $hasputtextbefore = 1;
861                 }
862     
863                 # remove punctuation at start
864                 $calculated =~ s/^( |;|:|\.|-)*//g;
865                 $blocres .= $calculated;
866               }
867             }
868             $blocres .= $textafter if $hasputtextbefore;
869             }       
870         }
871         else {
872             $blocres .= $isbdfield;
873         }
874     }
875     $res .= $blocres;
876     
877     $res =~ s/\{(.*?)\}//g;
878     $res =~ s/\\n/\n/g;
879     $res =~ s/\n/<br\/>/g;
880     
881     # remove empty ()
882     $res =~ s/\(\)//g;
883    
884     return $res;
885 }
886
887 =head2 GetBiblio
888
889 =over 4
890
891 ( $count, @results ) = &GetBiblio($biblionumber);
892
893 =back
894
895 =cut
896
897 sub GetBiblio {
898     my ($biblionumber) = @_;
899     my $dbh = C4::Context->dbh;
900     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber = ?");
901     my $count = 0;
902     my @results;
903     $sth->execute($biblionumber);
904     while ( my $data = $sth->fetchrow_hashref ) {
905         $results[$count] = $data;
906         $count++;
907     }    # while
908     $sth->finish;
909     return ( $count, @results );
910 }    # sub GetBiblio
911
912 =head2 GetBiblioItemInfosOf
913
914 =over 4
915
916 GetBiblioItemInfosOf(@biblioitemnumbers);
917
918 =back
919
920 =cut
921
922 sub GetBiblioItemInfosOf {
923     my @biblioitemnumbers = @_;
924
925     my $query = '
926         SELECT biblioitemnumber,
927             publicationyear,
928             itemtype
929         FROM biblioitems
930         WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
931     ';
932     return get_infos_of( $query, 'biblioitemnumber' );
933 }
934
935 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
936
937 =head2 GetMarcStructure
938
939 =over 4
940
941 $res = GetMarcStructure($forlibrarian,$frameworkcode);
942
943 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
944 $forlibrarian  :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
945 $frameworkcode : the framework code to read
946
947 =back
948
949 =cut
950
951 # cache for results of GetMarcStructure -- needed
952 # for batch jobs
953 our $marc_structure_cache;
954
955 sub GetMarcStructure {
956     my ( $forlibrarian, $frameworkcode ) = @_;
957     my $dbh=C4::Context->dbh;
958     $frameworkcode = "" unless $frameworkcode;
959
960     if (defined $marc_structure_cache and exists $marc_structure_cache->{$forlibrarian}->{$frameworkcode}) {
961         return $marc_structure_cache->{$forlibrarian}->{$frameworkcode};
962     }
963
964 #     my $sth = $dbh->prepare(
965 #         "SELECT COUNT(*) FROM marc_tag_structure WHERE frameworkcode=?");
966 #     $sth->execute($frameworkcode);
967 #     my ($total) = $sth->fetchrow;
968 #     $frameworkcode = "" unless ( $total > 0 );
969     my $sth = $dbh->prepare(
970         "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable 
971         FROM marc_tag_structure 
972         WHERE frameworkcode=? 
973         ORDER BY tagfield"
974     );
975     $sth->execute($frameworkcode);
976     my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
977
978     while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) =
979         $sth->fetchrow )
980     {
981         $res->{$tag}->{lib} =
982           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
983         $res->{$tag}->{tab}        = "";
984         $res->{$tag}->{mandatory}  = $mandatory;
985         $res->{$tag}->{repeatable} = $repeatable;
986     }
987
988     $sth = $dbh->prepare(
989         "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue 
990          FROM   marc_subfield_structure 
991          WHERE  frameworkcode=? 
992          ORDER BY tagfield,tagsubfield
993         "
994     );
995     
996     $sth->execute($frameworkcode);
997
998     my $subfield;
999     my $authorised_value;
1000     my $authtypecode;
1001     my $value_builder;
1002     my $kohafield;
1003     my $seealso;
1004     my $hidden;
1005     my $isurl;
1006     my $link;
1007     my $defaultvalue;
1008
1009     while (
1010         (
1011             $tag,          $subfield,      $liblibrarian,
1012             $libopac,      $tab,
1013             $mandatory,    $repeatable,    $authorised_value,
1014             $authtypecode, $value_builder, $kohafield,
1015             $seealso,      $hidden,        $isurl,
1016             $link,$defaultvalue
1017         )
1018         = $sth->fetchrow
1019       )
1020     {
1021         $res->{$tag}->{$subfield}->{lib} =
1022           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1023         $res->{$tag}->{$subfield}->{tab}              = $tab;
1024         $res->{$tag}->{$subfield}->{mandatory}        = $mandatory;
1025         $res->{$tag}->{$subfield}->{repeatable}       = $repeatable;
1026         $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
1027         $res->{$tag}->{$subfield}->{authtypecode}     = $authtypecode;
1028         $res->{$tag}->{$subfield}->{value_builder}    = $value_builder;
1029         $res->{$tag}->{$subfield}->{kohafield}        = $kohafield;
1030         $res->{$tag}->{$subfield}->{seealso}          = $seealso;
1031         $res->{$tag}->{$subfield}->{hidden}           = $hidden;
1032         $res->{$tag}->{$subfield}->{isurl}            = $isurl;
1033         $res->{$tag}->{$subfield}->{'link'}           = $link;
1034         $res->{$tag}->{$subfield}->{defaultvalue}     = $defaultvalue;
1035     }
1036
1037     $marc_structure_cache->{$forlibrarian}->{$frameworkcode} = $res;
1038
1039     return $res;
1040 }
1041
1042 =head2 GetUsedMarcStructure
1043
1044     the same function as GetMarcStructure except it just takes field
1045     in tab 0-9. (used field)
1046     
1047     my $results = GetUsedMarcStructure($frameworkcode);
1048     
1049     L<$results> is a ref to an array which each case containts a ref
1050     to a hash which each keys is the columns from marc_subfield_structure
1051     
1052     L<$frameworkcode> is the framework code. 
1053     
1054 =cut
1055
1056 sub GetUsedMarcStructure($){
1057     my $frameworkcode = shift || '';
1058     my $query         = qq/
1059         SELECT *
1060         FROM   marc_subfield_structure
1061         WHERE   tab > -1 
1062             AND frameworkcode = ?
1063         ORDER BY tagfield, tagsubfield
1064     /;
1065     my $sth = C4::Context->dbh->prepare($query);
1066     $sth->execute($frameworkcode);
1067     return $sth->fetchall_arrayref({});
1068 }
1069
1070 =head2 GetMarcFromKohaField
1071
1072 =over 4
1073
1074 ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
1075 Returns the MARC fields & subfields mapped to the koha field 
1076 for the given frameworkcode
1077
1078 =back
1079
1080 =cut
1081
1082 sub GetMarcFromKohaField {
1083     my ( $kohafield, $frameworkcode ) = @_;
1084     return 0, 0 unless $kohafield and defined $frameworkcode;
1085     my $relations = C4::Context->marcfromkohafield;
1086     return (
1087         $relations->{$frameworkcode}->{$kohafield}->[0],
1088         $relations->{$frameworkcode}->{$kohafield}->[1]
1089     );
1090 }
1091
1092 =head2 GetMarcBiblio
1093
1094 =over 4
1095
1096 my $record = GetMarcBiblio($biblionumber);
1097
1098 =back
1099
1100 Returns MARC::Record representing bib identified by
1101 C<$biblionumber>.  If no bib exists, returns undef.
1102 The MARC record contains both biblio & item data.
1103
1104 =cut
1105
1106 sub GetMarcBiblio {
1107     my $biblionumber = shift;
1108     my $dbh          = C4::Context->dbh;
1109     my $sth          =
1110       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1111     $sth->execute($biblionumber);
1112     my $row = $sth->fetchrow_hashref;
1113     my $marcxml = StripNonXmlChars($row->{'marcxml'});
1114      MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
1115     my $record = MARC::Record->new();
1116     if ($marcxml) {
1117         $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
1118         if ($@) {warn " problem with :$biblionumber : $@ \n$marcxml";}
1119 #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
1120         return $record;
1121     } else {
1122         return undef;
1123     }
1124 }
1125
1126 =head2 GetXmlBiblio
1127
1128 =over 4
1129
1130 my $marcxml = GetXmlBiblio($biblionumber);
1131
1132 Returns biblioitems.marcxml of the biblionumber passed in parameter.
1133 The XML contains both biblio & item datas
1134
1135 =back
1136
1137 =cut
1138
1139 sub GetXmlBiblio {
1140     my ( $biblionumber ) = @_;
1141     my $dbh = C4::Context->dbh;
1142     my $sth =
1143       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1144     $sth->execute($biblionumber);
1145     my ($marcxml) = $sth->fetchrow;
1146     return $marcxml;
1147 }
1148
1149 =head2 GetCOinSBiblio
1150
1151 =over 4
1152
1153 my $coins = GetCOinSBiblio($biblionumber);
1154
1155 Returns the COinS(a span) which can be included in a biblio record
1156
1157 =back
1158
1159 =cut
1160
1161 sub GetCOinSBiblio {
1162     my ( $biblionumber ) = @_;
1163     my $record = GetMarcBiblio($biblionumber);
1164
1165     # get the coin format
1166     my $pos7 = substr $record->leader(), 7,1;
1167     my $pos6 = substr $record->leader(), 6,1;
1168     my $mtx;
1169     my $genre;
1170     my ($aulast, $aufirst) = ('','');
1171     my $oauthors  = '';
1172     my $title     = '';
1173     my $subtitle  = '';
1174     my $pubyear   = '';
1175     my $isbn      = '';
1176     my $issn      = '';
1177     my $publisher = '';
1178
1179     if ( C4::Context->preference("marcflavour") eq "UNIMARC" ){
1180         my $fmts6;
1181         my $fmts7;
1182         %$fmts6 = (
1183                     'a' => 'book',
1184                     'b' => 'manuscript',
1185                     'c' => 'book',
1186                     'd' => 'manuscript',
1187                     'e' => 'map',
1188                     'f' => 'map',
1189                     'g' => 'film',
1190                     'i' => 'audioRecording',
1191                     'j' => 'audioRecording',
1192                     'k' => 'artwork',
1193                     'l' => 'document',
1194                     'm' => 'computerProgram',
1195                     'r' => 'document',
1196
1197                 );
1198         %$fmts7 = (
1199                     'a' => 'journalArticle',
1200                     's' => 'journal',
1201                 );
1202
1203         $genre =  $fmts6->{$pos6} ? $fmts6->{$pos6} : 'book' ;
1204
1205         if( $genre eq 'book' ){
1206             $genre =  $fmts7->{$pos7} if $fmts7->{$pos7};
1207         }
1208
1209         ##### We must transform mtx to a valable mtx and document type ####
1210         if( $genre eq 'book' ){
1211             $mtx = 'book';
1212         }elsif( $genre eq 'journal' ){
1213             $mtx = 'journal';
1214         }elsif( $genre eq 'journalArticle' ){
1215             $mtx = 'journal';
1216             $genre = 'article';
1217         }else{
1218             $mtx = 'dc';
1219         }
1220
1221         $genre = ($mtx eq 'dc') ? "&amp;rft.type=$genre" : "&amp;rft.genre=$genre";
1222
1223         # Setting datas
1224         $aulast     = $record->subfield('700','a');
1225         $aufirst    = $record->subfield('700','b');
1226         $oauthors   = "&amp;rft.au=$aufirst $aulast";
1227         # others authors
1228         if($record->field('200')){
1229             for my $au ($record->field('200')->subfield('g')){
1230                 $oauthors .= "&amp;rft.au=$au";
1231             }
1232         }
1233         $title      = ( $mtx eq 'dc' ) ? "&amp;rft.title=".$record->subfield('200','a') :
1234                                          "&amp;rft.title=".$record->subfield('200','a')."&amp;rft.btitle=".$record->subfield('200','a');
1235         $pubyear    = $record->subfield('210','d');
1236         $publisher  = $record->subfield('210','c');
1237         $isbn       = $record->subfield('010','a');
1238         $issn       = $record->subfield('011','a');
1239     }else{
1240         # MARC21 need some improve
1241         my $fmts;
1242         $mtx = 'book';
1243         $genre = "&amp;rft.genre=book";
1244
1245         # Setting datas
1246         if ($record->field('100')) {
1247             $oauthors .= "&amp;rft.au=".$record->subfield('100','a');
1248         }
1249         # others authors
1250         if($record->field('700')){
1251             for my $au ($record->field('700')->subfield('a')){
1252                 $oauthors .= "&amp;rft.au=$au";
1253             }
1254         }
1255         $title      = "&amp;rft.btitle=".$record->subfield('245','a');
1256         $subtitle   = $record->subfield('245', 'b') || '';
1257         $title .= $subtitle;
1258         $pubyear    = $record->subfield('260', 'c') || '';
1259         $publisher  = $record->subfield('260', 'b') || '';
1260         $isbn       = $record->subfield('020', 'a') || '';
1261         $issn       = $record->subfield('022', 'a') || '';
1262
1263     }
1264     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";
1265     $coins_value =~ s/(\ |&[^a])/\+/g;
1266     #<!-- 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="
1267
1268     return $coins_value;
1269 }
1270
1271 =head2 GetAuthorisedValueDesc
1272
1273 =over 4
1274
1275 my $subfieldvalue =get_authorised_value_desc(
1276     $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category, $opac);
1277 Retrieve the complete description for a given authorised value.
1278
1279 Now takes $category and $value pair too.
1280 my $auth_value_desc =GetAuthorisedValueDesc(
1281     '','', 'DVD' ,'','','CCODE');
1282
1283 If the optional $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1284
1285
1286 =back
1287
1288 =cut
1289
1290 sub GetAuthorisedValueDesc {
1291     my ( $tag, $subfield, $value, $framework, $tagslib, $category, $opac ) = @_;
1292     my $dbh = C4::Context->dbh;
1293
1294     if (!$category) {
1295
1296         return $value unless defined $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1297
1298 #---- branch
1299         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1300             return C4::Branch::GetBranchName($value);
1301         }
1302
1303 #---- itemtypes
1304         if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1305             return getitemtypeinfo($value)->{description};
1306         }
1307
1308 #---- "true" authorized value
1309         $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'}
1310     }
1311
1312     if ( $category ne "" ) {
1313         my $sth =
1314             $dbh->prepare(
1315                     "SELECT lib, lib_opac FROM authorised_values WHERE category = ? AND authorised_value = ?"
1316                     );
1317         $sth->execute( $category, $value );
1318         my $data = $sth->fetchrow_hashref;
1319         return ($opac && $data->{'lib_opac'}) ? $data->{'lib_opac'} : $data->{'lib'};
1320     }
1321     else {
1322         return $value;    # if nothing is found return the original value
1323     }
1324 }
1325
1326 =head2 GetMarcNotes
1327
1328 =over 4
1329
1330 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1331 Get all notes from the MARC record and returns them in an array.
1332 The note are stored in differents places depending on MARC flavour
1333
1334 =back
1335
1336 =cut
1337
1338 sub GetMarcNotes {
1339     my ( $record, $marcflavour ) = @_;
1340     my $scope;
1341     if ( $marcflavour eq "MARC21" ) {
1342         $scope = '5..';
1343     }
1344     else {    # assume unimarc if not marc21
1345         $scope = '3..';
1346     }
1347     my @marcnotes;
1348     my $note = "";
1349     my $tag  = "";
1350     my $marcnote;
1351     foreach my $field ( $record->field($scope) ) {
1352         my $value = $field->as_string();
1353         if ( $note ne "" ) {
1354             $marcnote = { marcnote => $note, };
1355             push @marcnotes, $marcnote;
1356             $note = $value;
1357         }
1358         if ( $note ne $value ) {
1359             $note = $note . " " . $value;
1360         }
1361     }
1362
1363     if ( $note ) {
1364         $marcnote = { marcnote => $note };
1365         push @marcnotes, $marcnote;    #load last tag into array
1366     }
1367     return \@marcnotes;
1368 }    # end GetMarcNotes
1369
1370 =head2 GetMarcSubjects
1371
1372 =over 4
1373
1374 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1375 Get all subjects from the MARC record and returns them in an array.
1376 The subjects are stored in differents places depending on MARC flavour
1377
1378 =back
1379
1380 =cut
1381
1382 sub GetMarcSubjects {
1383     my ( $record, $marcflavour ) = @_;
1384     my ( $mintag, $maxtag );
1385     if ( $marcflavour eq "MARC21" ) {
1386         $mintag = "600";
1387         $maxtag = "699";
1388     }
1389     else {    # assume unimarc if not marc21
1390         $mintag = "600";
1391         $maxtag = "611";
1392     }
1393     
1394     my @marcsubjects;
1395     my $subject = "";
1396     my $subfield = "";
1397     my $marcsubject;
1398
1399     foreach my $field ( $record->field('6..' )) {
1400         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1401         my @subfields_loop;
1402         my @subfields = $field->subfields();
1403         my $counter = 0;
1404         my @link_loop;
1405         # if there is an authority link, build the link with an= subfield9
1406                 my $found9=0;
1407         for my $subject_subfield (@subfields ) {
1408             # don't load unimarc subfields 3,4,5
1409             next if (($marcflavour eq "UNIMARC") and ($subject_subfield->[0] =~ /2|3|4|5/ ) );
1410             # don't load MARC21 subfields 2 (FIXME: any more subfields??)
1411             next if (($marcflavour eq "MARC21")  and ($subject_subfield->[0] =~ /2/ ) );
1412             my $code = $subject_subfield->[0];
1413             my $value = $subject_subfield->[1];
1414             my $linkvalue = $value;
1415             $linkvalue =~ s/(\(|\))//g;
1416             my $operator = " and " unless $counter==0;
1417             if ($code eq 9) {
1418                                 $found9 = 1;
1419                 @link_loop = ({'limit' => 'an' ,link => "$linkvalue" });
1420                         }
1421                         if (not $found9) {
1422                                 push @link_loop, {'limit' => 'su', link => $linkvalue, operator => $operator };
1423                         }
1424             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1425             # ignore $9
1426             my @this_link_loop = @link_loop;
1427             push @subfields_loop, {code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($subject_subfield->[0] eq 9 );
1428             $counter++;
1429         }
1430                 
1431         push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
1432         
1433     }
1434         return \@marcsubjects;
1435 }  #end getMARCsubjects
1436
1437 =head2 GetMarcAuthors
1438
1439 =over 4
1440
1441 authors = GetMarcAuthors($record,$marcflavour);
1442 Get all authors from the MARC record and returns them in an array.
1443 The authors are stored in differents places depending on MARC flavour
1444
1445 =back
1446
1447 =cut
1448
1449 sub GetMarcAuthors {
1450     my ( $record, $marcflavour ) = @_;
1451     my ( $mintag, $maxtag );
1452     # tagslib useful for UNIMARC author reponsabilities
1453     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.
1454     if ( $marcflavour eq "MARC21" ) {
1455         $mintag = "700";
1456         $maxtag = "720"; 
1457     }
1458     elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
1459         $mintag = "700";
1460         $maxtag = "712";
1461     }
1462     else {
1463         return;
1464     }
1465     my @marcauthors;
1466
1467     foreach my $field ( $record->fields ) {
1468         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1469         my @subfields_loop;
1470         my @link_loop;
1471         my @subfields = $field->subfields();
1472         my $count_auth = 0;
1473         # if there is an authority link, build the link with Koha-Auth-Number: subfield9
1474         my $subfield9 = $field->subfield('9');
1475         for my $authors_subfield (@subfields) {
1476             # don't load unimarc subfields 3, 5
1477             next if ($marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~ /3|5/ ) );
1478             my $subfieldcode = $authors_subfield->[0];
1479             my $value = $authors_subfield->[1];
1480             my $linkvalue = $value;
1481             $linkvalue =~ s/(\(|\))//g;
1482             my $operator = " and " unless $count_auth==0;
1483             # if we have an authority link, use that as the link, otherwise use standard searching
1484             if ($subfield9) {
1485                 @link_loop = ({'limit' => 'an' ,link => "$subfield9" });
1486             }
1487             else {
1488                 # reset $linkvalue if UNIMARC author responsibility
1489                 if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq "4")) {
1490                     $linkvalue = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
1491                 }
1492                 push @link_loop, {'limit' => 'au', link => $linkvalue, operator => $operator };
1493             }
1494             $value = GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ) if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~/4/));
1495             my @this_link_loop = @link_loop;
1496             my $separator = C4::Context->preference("authoritysep") unless $count_auth==0;
1497             push @subfields_loop, {code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($authors_subfield->[0] eq '9' );
1498             $count_auth++;
1499         }
1500         push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
1501     }
1502     return \@marcauthors;
1503 }
1504
1505 =head2 GetMarcUrls
1506
1507 =over 4
1508
1509 $marcurls = GetMarcUrls($record,$marcflavour);
1510 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1511 Assumes web resources (not uncommon in MARC21 to omit resource type ind) 
1512
1513 =back
1514
1515 =cut
1516
1517 sub GetMarcUrls {
1518     my ( $record, $marcflavour ) = @_;
1519
1520     my @marcurls;
1521     for my $field ( $record->field('856') ) {
1522         my $marcurl;
1523         my @notes;
1524         for my $note ( $field->subfield('z') ) {
1525             push @notes, { note => $note };
1526         }
1527         my @urls = $field->subfield('u');
1528         foreach my $url (@urls) {
1529             if ( $marcflavour eq 'MARC21' ) {
1530                 my $s3   = $field->subfield('3');
1531                 my $link = $field->subfield('y');
1532                 unless ( $url =~ /^\w+:/ ) {
1533                     if ( $field->indicator(1) eq '7' ) {
1534                         $url = $field->subfield('2') . "://" . $url;
1535                     } elsif ( $field->indicator(1) eq '1' ) {
1536                         $url = 'ftp://' . $url;
1537                     } else {
1538                         #  properly, this should be if ind1=4,
1539                         #  however we will assume http protocol since we're building a link.
1540                         $url = 'http://' . $url;
1541                     }
1542                 }
1543                 # TODO handle ind 2 (relationship)
1544                 $marcurl = {
1545                     MARCURL => $url,
1546                     notes   => \@notes,
1547                 };
1548                 $marcurl->{'linktext'} = $link || $s3 || C4::Context->preference('URLLinkText') || $url;
1549                 $marcurl->{'part'} = $s3 if ($link);
1550                 $marcurl->{'toc'} = 1 if ( defined($s3) && $s3 =~ /^[Tt]able/ );
1551             } else {
1552                 $marcurl->{'linktext'} = $field->subfield('2') || C4::Context->preference('URLLinkText') || $url;
1553                 $marcurl->{'MARCURL'} = $url;
1554             }
1555             push @marcurls, $marcurl;
1556         }
1557     }
1558     return \@marcurls;
1559 }
1560
1561 =head2 GetMarcSeries
1562
1563 =over 4
1564
1565 $marcseriesarray = GetMarcSeries($record,$marcflavour);
1566 Get all series from the MARC record and returns them in an array.
1567 The series are stored in differents places depending on MARC flavour
1568
1569 =back
1570
1571 =cut
1572
1573 sub GetMarcSeries {
1574     my ($record, $marcflavour) = @_;
1575     my ($mintag, $maxtag);
1576     if ($marcflavour eq "MARC21") {
1577         $mintag = "440";
1578         $maxtag = "490";
1579     } else {           # assume unimarc if not marc21
1580         $mintag = "600";
1581         $maxtag = "619";
1582     }
1583
1584     my @marcseries;
1585     my $subjct = "";
1586     my $subfield = "";
1587     my $marcsubjct;
1588
1589     foreach my $field ($record->field('440'), $record->field('490')) {
1590         my @subfields_loop;
1591         #my $value = $field->subfield('a');
1592         #$marcsubjct = {MARCSUBJCT => $value,};
1593         my @subfields = $field->subfields();
1594         #warn "subfields:".join " ", @$subfields;
1595         my $counter = 0;
1596         my @link_loop;
1597         for my $series_subfield (@subfields) {
1598             my $volume_number;
1599             undef $volume_number;
1600             # see if this is an instance of a volume
1601             if ($series_subfield->[0] eq 'v') {
1602                 $volume_number=1;
1603             }
1604
1605             my $code = $series_subfield->[0];
1606             my $value = $series_subfield->[1];
1607             my $linkvalue = $value;
1608             $linkvalue =~ s/(\(|\))//g;
1609             my $operator = " and " unless $counter==0;
1610             push @link_loop, {link => $linkvalue, operator => $operator };
1611             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
1612             if ($volume_number) {
1613             push @subfields_loop, {volumenum => $value};
1614             }
1615             else {
1616             push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number};
1617             }
1618             $counter++;
1619         }
1620         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1621         #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1622         #push @marcsubjcts, $marcsubjct;
1623         #$subjct = $value;
1624
1625     }
1626     my $marcseriessarray=\@marcseries;
1627     return $marcseriessarray;
1628 }  #end getMARCseriess
1629
1630 =head2 GetFrameworkCode
1631
1632 =over 4
1633
1634     $frameworkcode = GetFrameworkCode( $biblionumber )
1635
1636 =back
1637
1638 =cut
1639
1640 sub GetFrameworkCode {
1641     my ( $biblionumber ) = @_;
1642     my $dbh = C4::Context->dbh;
1643     my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
1644     $sth->execute($biblionumber);
1645     my ($frameworkcode) = $sth->fetchrow;
1646     return $frameworkcode;
1647 }
1648
1649 =head2 GetPublisherNameFromIsbn
1650
1651     $name = GetPublishercodeFromIsbn($isbn);
1652     if(defined $name){
1653         ...
1654     }
1655
1656 =cut
1657
1658 sub GetPublisherNameFromIsbn($){
1659     my $isbn = shift;
1660     $isbn =~ s/[- _]//g;
1661     $isbn =~ s/^0*//;
1662     my @codes = (split '-', DisplayISBN($isbn));
1663     my $code = $codes[0].$codes[1].$codes[2];
1664     my $dbh  = C4::Context->dbh;
1665     my $query = qq{
1666         SELECT distinct publishercode
1667         FROM   biblioitems
1668         WHERE  isbn LIKE ?
1669         AND    publishercode IS NOT NULL
1670         LIMIT 1
1671     };
1672     my $sth = $dbh->prepare($query);
1673     $sth->execute("$code%");
1674     my $name = $sth->fetchrow;
1675     return $name if length $name;
1676     return undef;
1677 }
1678
1679 =head2 TransformKohaToMarc
1680
1681 =over 4
1682
1683     $record = TransformKohaToMarc( $hash )
1684     This function builds partial MARC::Record from a hash
1685     Hash entries can be from biblio or biblioitems.
1686     This function is called in acquisition module, to create a basic catalogue entry from user entry
1687
1688 =back
1689
1690 =cut
1691
1692 sub TransformKohaToMarc {
1693     my ( $hash ) = @_;
1694     my $sth = C4::Context->dbh->prepare(
1695         "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
1696     );
1697     my $record = MARC::Record->new();
1698     SetMarcUnicodeFlag($record, C4::Context->preference("marcflavour"));
1699     foreach (keys %{$hash}) {
1700         &TransformKohaToMarcOneField( $sth, $record, $_, $hash->{$_}, '' );
1701     }
1702     return $record;
1703 }
1704
1705 =head2 TransformKohaToMarcOneField
1706
1707 =over 4
1708
1709     $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
1710
1711 =back
1712
1713 =cut
1714
1715 sub TransformKohaToMarcOneField {
1716     my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
1717     $frameworkcode='' unless $frameworkcode;
1718     my $tagfield;
1719     my $tagsubfield;
1720
1721     if ( !defined $sth ) {
1722         my $dbh = C4::Context->dbh;
1723         $sth = $dbh->prepare(
1724             "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
1725         );
1726     }
1727     $sth->execute( $frameworkcode, $kohafieldname );
1728     if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
1729         my $tag = $record->field($tagfield);
1730         if ($tag) {
1731             $tag->update( $tagsubfield => $value );
1732             $record->delete_field($tag);
1733             $record->insert_fields_ordered($tag);
1734         }
1735         else {
1736             $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
1737         }
1738     }
1739     return $record;
1740 }
1741
1742 =head2 TransformHtmlToXml
1743
1744 =over 4
1745
1746 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type )
1747
1748 $auth_type contains :
1749 - nothing : rebuild a biblio, un UNIMARC the encoding is in 100$a pos 26/27
1750 - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
1751 - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
1752
1753 =back
1754
1755 =cut
1756
1757 sub TransformHtmlToXml {
1758     my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
1759     my $xml = MARC::File::XML::header('UTF-8');
1760     $xml .= "<record>\n";
1761     $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
1762     MARC::File::XML->default_record_format($auth_type);
1763     # in UNIMARC, field 100 contains the encoding
1764     # check that there is one, otherwise the 
1765     # MARC::Record->new_from_xml will fail (and Koha will die)
1766     my $unimarc_and_100_exist=0;
1767     $unimarc_and_100_exist=1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
1768     my $prevvalue;
1769     my $prevtag = -1;
1770     my $first   = 1;
1771     my $j       = -1;
1772     for ( my $i = 0 ; $i < @$tags ; $i++ ) {
1773         if (C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a") {
1774             # if we have a 100 field and it's values are not correct, skip them.
1775             # if we don't have any valid 100 field, we will create a default one at the end
1776             my $enc = substr( @$values[$i], 26, 2 );
1777             if ($enc eq '01' or $enc eq '50' or $enc eq '03') {
1778                 $unimarc_and_100_exist=1;
1779             } else {
1780                 next;
1781             }
1782         }
1783         @$values[$i] =~ s/&/&amp;/g;
1784         @$values[$i] =~ s/</&lt;/g;
1785         @$values[$i] =~ s/>/&gt;/g;
1786         @$values[$i] =~ s/"/&quot;/g;
1787         @$values[$i] =~ s/'/&apos;/g;
1788 #         if ( !utf8::is_utf8( @$values[$i] ) ) {
1789 #             utf8::decode( @$values[$i] );
1790 #         }
1791         if ( ( @$tags[$i] ne $prevtag ) ) {
1792             $j++ unless ( @$tags[$i] eq "" );
1793             if ( !$first ) {
1794                 $xml .= "</datafield>\n";
1795                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
1796                     && ( @$values[$i] ne "" ) )
1797                 {
1798                     my $ind1 = _default_ind_to_space(substr( @$indicator[$j], 0, 1 ));
1799                     my $ind2;
1800                     if ( @$indicator[$j] ) {
1801                         $ind2 = _default_ind_to_space(substr( @$indicator[$j], 1, 1 ));
1802                     }
1803                     else {
1804                         warn "Indicator in @$tags[$i] is empty";
1805                         $ind2 = " ";
1806                     }
1807                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1808                     $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1809                     $first = 0;
1810                 }
1811                 else {
1812                     $first = 1;
1813                 }
1814             }
1815             else {
1816                 if ( @$values[$i] ne "" ) {
1817
1818                     # leader
1819                     if ( @$tags[$i] eq "000" ) {
1820                         $xml .= "<leader>@$values[$i]</leader>\n";
1821                         $first = 1;
1822
1823                         # rest of the fixed fields
1824                     }
1825                     elsif ( @$tags[$i] < 10 ) {
1826                         $xml .= "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
1827                         $first = 1;
1828                     }
1829                     else {
1830                         my $ind1 = _default_ind_to_space( substr( @$indicator[$j], 0, 1 ) );
1831                         my $ind2 = _default_ind_to_space( substr( @$indicator[$j], 1, 1 ) );
1832                         $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1833                         $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1834                         $first = 0;
1835                     }
1836                 }
1837             }
1838         }
1839         else {    # @$tags[$i] eq $prevtag
1840             if ( @$values[$i] eq "" ) {
1841             }
1842             else {
1843                 if ($first) {
1844                     my $ind1 = _default_ind_to_space( substr( @$indicator[$j], 0, 1 ) );
1845                     my $ind2 = _default_ind_to_space( substr( @$indicator[$j], 1, 1 ) );
1846                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1847                     $first = 0;
1848                 }
1849                 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1850             }
1851         }
1852         $prevtag = @$tags[$i];
1853     }
1854     $xml .= "</datafield>\n" if @$tags > 0;
1855     if (C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist) {
1856 #     warn "SETTING 100 for $auth_type";
1857         my $string = strftime( "%Y%m%d", localtime(time) );
1858         # set 50 to position 26 is biblios, 13 if authorities
1859         my $pos=26;
1860         $pos=13 if $auth_type eq 'UNIMARCAUTH';
1861         $string = sprintf( "%-*s", 35, $string );
1862         substr( $string, $pos , 6, "50" );
1863         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
1864         $xml .= "<subfield code=\"a\">$string</subfield>\n";
1865         $xml .= "</datafield>\n";
1866     }
1867     $xml .= "</record>\n";
1868     $xml .= MARC::File::XML::footer();
1869     return $xml;
1870 }
1871
1872 =head2 _default_ind_to_space
1873
1874 Passed what should be an indicator returns a space
1875 if its undefined or zero length
1876
1877 =cut
1878
1879 sub _default_ind_to_space {
1880     my $s = shift;
1881     if (!defined $s || $s eq q{}) {
1882         return ' ';
1883     }
1884     return $s;
1885 }
1886
1887 =head2 TransformHtmlToMarc
1888
1889     L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
1890     L<$params> is a ref to an array as below:
1891     {
1892         'tag_010_indicator1_531951' ,
1893         'tag_010_indicator2_531951' ,
1894         'tag_010_code_a_531951_145735' ,
1895         'tag_010_subfield_a_531951_145735' ,
1896         'tag_200_indicator1_873510' ,
1897         'tag_200_indicator2_873510' ,
1898         'tag_200_code_a_873510_673465' ,
1899         'tag_200_subfield_a_873510_673465' ,
1900         'tag_200_code_b_873510_704318' ,
1901         'tag_200_subfield_b_873510_704318' ,
1902         'tag_200_code_e_873510_280822' ,
1903         'tag_200_subfield_e_873510_280822' ,
1904         'tag_200_code_f_873510_110730' ,
1905         'tag_200_subfield_f_873510_110730' ,
1906     }
1907     L<$cgi> is the CGI object which containts the value.
1908     L<$record> is the MARC::Record object.
1909
1910 =cut
1911
1912 sub TransformHtmlToMarc {
1913     my $params = shift;
1914     my $cgi    = shift;
1915
1916     # explicitly turn on the UTF-8 flag for all
1917     # 'tag_' parameters to avoid incorrect character
1918     # conversion later on
1919     my $cgi_params = $cgi->Vars;
1920     foreach my $param_name (keys %$cgi_params) {
1921         if ($param_name =~ /^tag_/) {
1922             my $param_value = $cgi_params->{$param_name};
1923             if (utf8::decode($param_value)) {
1924                 $cgi_params->{$param_name} = $param_value;
1925             } 
1926             # FIXME - need to do something if string is not valid UTF-8
1927         }
1928     }
1929    
1930     # creating a new record
1931     my $record  = MARC::Record->new();
1932     my $i=0;
1933     my @fields;
1934     while ($params->[$i]){ # browse all CGI params
1935         my $param = $params->[$i];
1936         my $newfield=0;
1937         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
1938         if ($param eq 'biblionumber') {
1939             my ( $biblionumbertagfield, $biblionumbertagsubfield ) =
1940                 &GetMarcFromKohaField( "biblio.biblionumber", '' );
1941             if ($biblionumbertagfield < 10) {
1942                 $newfield = MARC::Field->new(
1943                     $biblionumbertagfield,
1944                     $cgi->param($param),
1945                 );
1946             } else {
1947                 $newfield = MARC::Field->new(
1948                     $biblionumbertagfield,
1949                     '',
1950                     '',
1951                     "$biblionumbertagsubfield" => $cgi->param($param),
1952                 );
1953             }
1954             push @fields,$newfield if($newfield);
1955         } 
1956         elsif ($param =~ /^tag_(\d*)_indicator1_/){ # new field start when having 'input name="..._indicator1_..."
1957             my $tag  = $1;
1958             
1959             my $ind1 = _default_ind_to_space(substr($cgi->param($param),          0, 1));
1960             my $ind2 = _default_ind_to_space(substr($cgi->param($params->[$i+1]), 0, 1));
1961             $newfield=0;
1962             my $j=$i+2;
1963             
1964             if($tag < 10){ # no code for theses fields
1965     # in MARC editor, 000 contains the leader.
1966                 if ($tag eq '000' ) {
1967                     $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==24;
1968     # between 001 and 009 (included)
1969                 } elsif ($cgi->param($params->[$j+1]) ne '') {
1970                     $newfield = MARC::Field->new(
1971                         $tag,
1972                         $cgi->param($params->[$j+1]),
1973                     );
1974                 }
1975     # > 009, deal with subfields
1976             } else {
1977                 while(defined $params->[$j] && $params->[$j] =~ /_code_/){ # browse all it's subfield
1978                     my $inner_param = $params->[$j];
1979                     if ($newfield){
1980                         if($cgi->param($params->[$j+1]) ne ''){  # only if there is a value (code => value)
1981                             $newfield->add_subfields(
1982                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
1983                             );
1984                         }
1985                     } else {
1986                         if ( $cgi->param($params->[$j+1]) ne '' ) { # creating only if there is a value (code => value)
1987                             $newfield = MARC::Field->new(
1988                                 $tag,
1989                                 $ind1,
1990                                 $ind2,
1991                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1]),
1992                             );
1993                         }
1994                     }
1995                     $j+=2;
1996                 }
1997             }
1998             push @fields,$newfield if($newfield);
1999         }
2000         $i++;
2001     }
2002     
2003     $record->append_fields(@fields);
2004     return $record;
2005 }
2006
2007 # cache inverted MARC field map
2008 our $inverted_field_map;
2009
2010 =head2 TransformMarcToKoha
2011
2012 =over 4
2013
2014     $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2015
2016 =back
2017
2018 Extract data from a MARC bib record into a hashref representing
2019 Koha biblio, biblioitems, and items fields. 
2020
2021 =cut
2022 sub TransformMarcToKoha {
2023     my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
2024
2025     my $result;
2026     $limit_table=$limit_table||0;
2027     $frameworkcode = '' unless defined $frameworkcode;
2028     
2029     unless (defined $inverted_field_map) {
2030         $inverted_field_map = _get_inverted_marc_field_map();
2031     }
2032
2033     my %tables = ();
2034     if ( defined $limit_table && $limit_table eq 'items') {
2035         $tables{'items'} = 1;
2036     } else {
2037         $tables{'items'} = 1;
2038         $tables{'biblio'} = 1;
2039         $tables{'biblioitems'} = 1;
2040     }
2041
2042     # traverse through record
2043     MARCFIELD: foreach my $field ($record->fields()) {
2044         my $tag = $field->tag();
2045         next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
2046         if ($field->is_control_field()) {
2047             my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
2048             ENTRY: foreach my $entry (@{ $kohafields }) {
2049                 my ($subfield, $table, $column) = @{ $entry };
2050                 next ENTRY unless exists $tables{$table};
2051                 my $key = _disambiguate($table, $column);
2052                 if ($result->{$key}) {
2053                     unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($field->data() eq "")) {
2054                         $result->{$key} .= " | " . $field->data();
2055                     }
2056                 } else {
2057                     $result->{$key} = $field->data();
2058                 }
2059             }
2060         } else {
2061             # deal with subfields
2062             MARCSUBFIELD: foreach my $sf ($field->subfields()) {
2063                 my $code = $sf->[0];
2064                 next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
2065                 my $value = $sf->[1];
2066                 SFENTRY: foreach my $entry (@{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} }) {
2067                     my ($table, $column) = @{ $entry };
2068                     next SFENTRY unless exists $tables{$table};
2069                     my $key = _disambiguate($table, $column);
2070                     if ($result->{$key}) {
2071                         unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
2072                             $result->{$key} .= " | " . $value;
2073                         }
2074                     } else {
2075                         $result->{$key} = $value;
2076                     }
2077                 }
2078             }
2079         }
2080     }
2081
2082     # modify copyrightdate to keep only the 1st year found
2083     if (exists $result->{'copyrightdate'}) {
2084         my $temp = $result->{'copyrightdate'};
2085         $temp =~ m/c(\d\d\d\d)/;
2086         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
2087             $result->{'copyrightdate'} = $1;
2088         }
2089         else {                      # if no cYYYY, get the 1st date.
2090             $temp =~ m/(\d\d\d\d)/;
2091             $result->{'copyrightdate'} = $1;
2092         }
2093     }
2094
2095     # modify publicationyear to keep only the 1st year found
2096     if (exists $result->{'publicationyear'}) {
2097         my $temp = $result->{'publicationyear'};
2098         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
2099             $result->{'publicationyear'} = $1;
2100         }
2101         else {                      # if no cYYYY, get the 1st date.
2102             $temp =~ m/(\d\d\d\d)/;
2103             $result->{'publicationyear'} = $1;
2104         }
2105     }
2106
2107     return $result;
2108 }
2109
2110 sub _get_inverted_marc_field_map {
2111     my $field_map = {};
2112     my $relations = C4::Context->marcfromkohafield;
2113
2114     foreach my $frameworkcode (keys %{ $relations }) {
2115         foreach my $kohafield (keys %{ $relations->{$frameworkcode} }) {
2116             next unless @{ $relations->{$frameworkcode}->{$kohafield} }; # not all columns are mapped to MARC tag & subfield
2117             my $tag = $relations->{$frameworkcode}->{$kohafield}->[0];
2118             my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
2119             my ($table, $column) = split /[.]/, $kohafield, 2;
2120             push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
2121             push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
2122         }
2123     }
2124     return $field_map;
2125 }
2126
2127 =head2 _disambiguate
2128
2129 =over 4
2130
2131 $newkey = _disambiguate($table, $field);
2132
2133 This is a temporary hack to distinguish between the
2134 following sets of columns when using TransformMarcToKoha.
2135
2136 items.cn_source & biblioitems.cn_source
2137 items.cn_sort & biblioitems.cn_sort
2138
2139 Columns that are currently NOT distinguished (FIXME
2140 due to lack of time to fully test) are:
2141
2142 biblio.notes and biblioitems.notes
2143 biblionumber
2144 timestamp
2145 biblioitemnumber
2146
2147 FIXME - this is necessary because prefixing each column
2148 name with the table name would require changing lots
2149 of code and templates, and exposing more of the DB
2150 structure than is good to the UI templates, particularly
2151 since biblio and bibloitems may well merge in a future
2152 version.  In the future, it would also be good to 
2153 separate DB access and UI presentation field names
2154 more.
2155
2156 =back
2157
2158 =cut
2159
2160 sub CountItemsIssued {
2161   my ( $biblionumber )  = @_;
2162   my $dbh = C4::Context->dbh;
2163   my $sth = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
2164   $sth->execute( $biblionumber );
2165   my $row = $sth->fetchrow_hashref();
2166   return $row->{'issuedCount'};
2167 }
2168
2169 sub _disambiguate {
2170     my ($table, $column) = @_;
2171     if ($column eq "cn_sort" or $column eq "cn_source") {
2172         return $table . '.' . $column;
2173     } else {
2174         return $column;
2175     }
2176
2177 }
2178
2179 =head2 get_koha_field_from_marc
2180
2181 =over 4
2182
2183 $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2184
2185 Internal function to map data from the MARC record to a specific non-MARC field.
2186 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2187
2188 =back
2189
2190 =cut
2191
2192 sub get_koha_field_from_marc {
2193     my ($koha_table,$koha_column,$record,$frameworkcode) = @_;
2194     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table.'.'.$koha_column, $frameworkcode );  
2195     my $kohafield;
2196     foreach my $field ( $record->field($tagfield) ) {
2197         if ( $field->tag() < 10 ) {
2198             if ( $kohafield ) {
2199                 $kohafield .= " | " . $field->data();
2200             }
2201             else {
2202                 $kohafield = $field->data();
2203             }
2204         }
2205         else {
2206             if ( $field->subfields ) {
2207                 my @subfields = $field->subfields();
2208                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2209                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2210                         if ( $kohafield ) {
2211                             $kohafield .=
2212                               " | " . $subfields[$subfieldcount][1];
2213                         }
2214                         else {
2215                             $kohafield =
2216                               $subfields[$subfieldcount][1];
2217                         }
2218                     }
2219                 }
2220             }
2221         }
2222     }
2223     return $kohafield;
2224
2225
2226
2227 =head2 TransformMarcToKohaOneField
2228
2229 =over 4
2230
2231 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2232
2233 =back
2234
2235 =cut
2236
2237 sub TransformMarcToKohaOneField {
2238
2239     # FIXME ? if a field has a repeatable subfield that is used in old-db,
2240     # only the 1st will be retrieved...
2241     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2242     my $res = "";
2243     my ( $tagfield, $subfield ) =
2244       GetMarcFromKohaField( $kohatable . "." . $kohafield,
2245         $frameworkcode );
2246     foreach my $field ( $record->field($tagfield) ) {
2247         if ( $field->tag() < 10 ) {
2248             if ( $result->{$kohafield} ) {
2249                 $result->{$kohafield} .= " | " . $field->data();
2250             }
2251             else {
2252                 $result->{$kohafield} = $field->data();
2253             }
2254         }
2255         else {
2256             if ( $field->subfields ) {
2257                 my @subfields = $field->subfields();
2258                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2259                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2260                         if ( $result->{$kohafield} ) {
2261                             $result->{$kohafield} .=
2262                               " | " . $subfields[$subfieldcount][1];
2263                         }
2264                         else {
2265                             $result->{$kohafield} =
2266                               $subfields[$subfieldcount][1];
2267                         }
2268                     }
2269                 }
2270             }
2271         }
2272     }
2273     return $result;
2274 }
2275
2276 =head1  OTHER FUNCTIONS
2277
2278
2279 =head2 PrepareItemrecordDisplay
2280
2281 =over 4
2282
2283 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
2284
2285 Returns a hash with all the fields for Display a given item data in a template
2286
2287 The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
2288
2289 =back
2290
2291 =cut
2292
2293 sub PrepareItemrecordDisplay {
2294
2295     my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2296
2297     my $dbh = C4::Context->dbh;
2298     $frameworkcode = &GetFrameworkCode( $bibnum ) if $bibnum;
2299     my ( $itemtagfield, $itemtagsubfield ) =
2300       &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2301     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2302     # return nothing if we don't have found an existing framework.
2303     return "" unless $tagslib;
2304     my $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum) if ($itemnum);
2305     my @loop_data;
2306     my $authorised_values_sth =
2307       $dbh->prepare(
2308 "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"
2309       );
2310     foreach my $tag ( sort keys %{$tagslib} ) {
2311         my $previous_tag = '';
2312         if ( $tag ne '' ) {
2313             # loop through each subfield
2314             my $cntsubf;
2315             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2316                 next if ( subfield_is_koha_internal_p($subfield) );
2317                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2318                 my %subfield_data;
2319                 $subfield_data{tag}           = $tag;
2320                 $subfield_data{subfield}      = $subfield;
2321                 $subfield_data{countsubfield} = $cntsubf++;
2322                 $subfield_data{kohafield}     =
2323                   $tagslib->{$tag}->{$subfield}->{'kohafield'};
2324
2325          #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2326                 $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib};
2327                 $subfield_data{mandatory} =
2328                   $tagslib->{$tag}->{$subfield}->{mandatory};
2329                 $subfield_data{repeatable} =
2330                   $tagslib->{$tag}->{$subfield}->{repeatable};
2331                 $subfield_data{hidden} = "display:none"
2332                   if $tagslib->{$tag}->{$subfield}->{hidden};
2333                   my ( $x, $value );
2334                   if ($itemrecord) {
2335                       ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord );
2336                   }
2337                   if (!defined $value) {
2338                       $value = q||;
2339                   }
2340                   $value =~ s/"/&quot;/g;
2341
2342                 # search for itemcallnumber if applicable
2343                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2344                     'items.itemcallnumber'
2345                     && C4::Context->preference('itemcallnumber') )
2346                 {
2347                     my $CNtag =
2348                       substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2349                     my $CNsubfield =
2350                       substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2351                     my $temp = $itemrecord->field($CNtag) if ($itemrecord);
2352                     if ($temp) {
2353                         $value = $temp->subfield($CNsubfield);
2354                     }
2355                 }
2356                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2357                     'items.itemcallnumber'
2358                     && $defaultvalues && $defaultvalues->{'callnumber'} )
2359                 {
2360                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2361                     unless ($temp) {
2362                         $value = $defaultvalues->{'callnumber'} if $defaultvalues;
2363                     }
2364                 }
2365                 if ( ($tagslib->{$tag}->{$subfield}->{kohafield} eq
2366                     'items.holdingbranch' ||
2367                     $tagslib->{$tag}->{$subfield}->{kohafield} eq
2368                     'items.homebranch')          
2369                     && $defaultvalues && $defaultvalues->{'branchcode'} )
2370                 {
2371                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2372                     unless ($temp) {
2373                         $value = $defaultvalues->{branchcode}  if $defaultvalues;
2374                     }
2375                 }
2376                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2377                     my @authorised_values;
2378                     my %authorised_lib;
2379
2380                     # builds list, depending on authorised value...
2381                     #---- branch
2382                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
2383                         "branches" )
2384                     {
2385                         if ( ( C4::Context->preference("IndependantBranches") )
2386                             && ( C4::Context->userenv->{flags} % 2 != 1 ) )
2387                         {
2388                             my $sth =
2389                               $dbh->prepare(
2390                                 "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname"
2391                               );
2392                             $sth->execute( C4::Context->userenv->{branch} );
2393                             push @authorised_values, ""
2394                               unless (
2395                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2396                             while ( my ( $branchcode, $branchname ) =
2397                                 $sth->fetchrow_array )
2398                             {
2399                                 push @authorised_values, $branchcode;
2400                                 $authorised_lib{$branchcode} = $branchname;
2401                             }
2402                         }
2403                         else {
2404                             my $sth =
2405                               $dbh->prepare(
2406                                 "SELECT branchcode,branchname FROM branches ORDER BY branchname"
2407                               );
2408                             $sth->execute;
2409                             push @authorised_values, ""
2410                               unless (
2411                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2412                             while ( my ( $branchcode, $branchname ) =
2413                                 $sth->fetchrow_array )
2414                             {
2415                                 push @authorised_values, $branchcode;
2416                                 $authorised_lib{$branchcode} = $branchname;
2417                             }
2418                         }
2419
2420                         #----- itemtypes
2421                     }
2422                     elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
2423                         "itemtypes" )
2424                     {
2425                         my $sth =
2426                           $dbh->prepare(
2427                             "SELECT itemtype,description FROM itemtypes ORDER BY description"
2428                           );
2429                         $sth->execute;
2430                         push @authorised_values, ""
2431                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2432                         while ( my ( $itemtype, $description ) =
2433                             $sth->fetchrow_array )
2434                         {
2435                             push @authorised_values, $itemtype;
2436                             $authorised_lib{$itemtype} = $description;
2437                         }
2438
2439                         #---- "true" authorised value
2440                     }
2441                     else {
2442                         $authorised_values_sth->execute(
2443                             $tagslib->{$tag}->{$subfield}->{authorised_value} );
2444                         push @authorised_values, ""
2445                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2446                         while ( my ( $value, $lib ) =
2447                             $authorised_values_sth->fetchrow_array )
2448                         {
2449                             push @authorised_values, $value;
2450                             $authorised_lib{$value} = $lib;
2451                         }
2452                     }
2453                     $subfield_data{marc_value} = CGI::scrolling_list(
2454                         -name     => 'field_value',
2455                         -values   => \@authorised_values,
2456                         -default  => "$value",
2457                         -labels   => \%authorised_lib,
2458                         -size     => 1,
2459                         -tabindex => '',
2460                         -multiple => 0,
2461                     );
2462                 }
2463                 else {
2464                     $subfield_data{marc_value} =
2465 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=\"50\" maxlength=\"255\" />";
2466                 }
2467                 push( @loop_data, \%subfield_data );
2468             }
2469         }
2470     }
2471     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2472       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2473     return {
2474         'itemtagfield'    => $itemtagfield,
2475         'itemtagsubfield' => $itemtagsubfield,
2476         'itemnumber'      => $itemnumber,
2477         'iteminformation' => \@loop_data
2478     };
2479 }
2480 #"
2481
2482 #
2483 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2484 # at the same time
2485 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2486 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2487 # =head2 ModZebrafiles
2488
2489 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2490
2491 # =cut
2492
2493 # sub ModZebrafiles {
2494
2495 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2496
2497 #     my $op;
2498 #     my $zebradir =
2499 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2500 #     unless ( opendir( DIR, "$zebradir" ) ) {
2501 #         warn "$zebradir not found";
2502 #         return;
2503 #     }
2504 #     closedir DIR;
2505 #     my $filename = $zebradir . $biblionumber;
2506
2507 #     if ($record) {
2508 #         open( OUTPUT, ">", $filename . ".xml" );
2509 #         print OUTPUT $record;
2510 #         close OUTPUT;
2511 #     }
2512 # }
2513
2514 =head2 ModZebra
2515
2516 =over 4
2517
2518 ModZebra( $biblionumber, $op, $server, $oldRecord, $newRecord );
2519
2520     $biblionumber is the biblionumber we want to index
2521     $op is specialUpdate or delete, and is used to know what we want to do
2522     $server is the server that we want to update
2523     $oldRecord is the MARC::Record containing the previous version of the record.  This is used only when 
2524       NoZebra=1, as NoZebra indexing needs to know the previous version of a record in order to
2525       do an update.
2526     $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.
2527     
2528 =back
2529
2530 =cut
2531
2532 sub ModZebra {
2533 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2534     my ( $biblionumber, $op, $server, $oldRecord, $newRecord ) = @_;
2535     my $dbh=C4::Context->dbh;
2536
2537     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2538     # at the same time
2539     # replaced by a zebraqueue table, that is filled with ModZebra to run.
2540     # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2541
2542     if (C4::Context->preference("NoZebra")) {
2543         # lock the nozebra table : we will read index lines, update them in Perl process
2544         # and write everything in 1 transaction.
2545         # lock the table to avoid someone else overwriting what we are doing
2546         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE, auth_subfield_structure READ');
2547         my %result; # the result hash that will be built by deletion / add, and written on mySQL at the end, to improve speed
2548         if ($op eq 'specialUpdate') {
2549             # OK, we have to add or update the record
2550             # 1st delete (virtually, in indexes), if record actually exists
2551             if ($oldRecord) { 
2552                 %result = _DelBiblioNoZebra($biblionumber,$oldRecord,$server);
2553             }
2554             # ... add the record
2555             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
2556         } else {
2557             # it's a deletion, delete the record...
2558             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2559             %result=_DelBiblioNoZebra($biblionumber,$oldRecord,$server);
2560         }
2561         # ok, now update the database...
2562         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2563         foreach my $key (keys %result) {
2564             foreach my $index (keys %{$result{$key}}) {
2565                 $sth->execute($result{$key}->{$index}, $server, $key, $index);
2566             }
2567         }
2568         $dbh->do('UNLOCK TABLES');
2569     } else {
2570         #
2571         # we use zebra, just fill zebraqueue table
2572         #
2573         my $check_sql = "SELECT COUNT(*) FROM zebraqueue 
2574                          WHERE server = ?
2575                          AND   biblio_auth_number = ?
2576                          AND   operation = ?
2577                          AND   done = 0";
2578         my $check_sth = $dbh->prepare_cached($check_sql);
2579         $check_sth->execute($server, $biblionumber, $op);
2580         my ($count) = $check_sth->fetchrow_array;
2581         $check_sth->finish();
2582         if ($count == 0) {
2583             my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2584             $sth->execute($biblionumber,$server,$op);
2585             $sth->finish;
2586         }
2587     }
2588 }
2589
2590 =head2 GetNoZebraIndexes
2591
2592     %indexes = GetNoZebraIndexes;
2593     
2594     return the data from NoZebraIndexes syspref.
2595
2596 =cut
2597
2598 sub GetNoZebraIndexes {
2599     my $no_zebra_indexes = C4::Context->preference('NoZebraIndexes');
2600     my %indexes;
2601     INDEX: foreach my $line (split /['"],[\n\r]*/,$no_zebra_indexes) {
2602         $line =~ /(.*)=>(.*)/;
2603         my $index = $1; # initial ' or " is removed afterwards
2604         my $fields = $2;
2605         $index =~ s/'|"|\s//g;
2606         $fields =~ s/'|"|\s//g;
2607         $indexes{$index}=$fields;
2608     }
2609     return %indexes;
2610 }
2611
2612 =head1 INTERNAL FUNCTIONS
2613
2614 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2615
2616     function to delete a biblio in NoZebra indexes
2617     This function does NOT delete anything in database : it reads all the indexes entries
2618     that have to be deleted & delete them in the hash
2619     The SQL part is done either :
2620     - after the Add if we are modifying a biblio (delete + add again)
2621     - immediatly after this sub if we are doing a true deletion.
2622     $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2623
2624 =cut
2625
2626
2627 sub _DelBiblioNoZebra {
2628     my ($biblionumber, $record, $server)=@_;
2629     
2630     # Get the indexes
2631     my $dbh = C4::Context->dbh;
2632     # Get the indexes
2633     my %index;
2634     my $title;
2635     if ($server eq 'biblioserver') {
2636         %index=GetNoZebraIndexes;
2637         # get title of the record (to store the 10 first letters with the index)
2638         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title', ''); # FIXME: should be GetFrameworkCode($biblionumber) ??
2639         $title = lc($record->subfield($titletag,$titlesubfield));
2640     } else {
2641         # for authorities, the "title" is the $a mainentry
2642         my ($auth_type_tag, $auth_type_sf) = C4::AuthoritiesMarc::get_auth_type_location();
2643         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield($auth_type_tag, $auth_type_sf));
2644         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2645         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2646         $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
2647         $index{'mainentry'}    = $authref->{'auth_tag_to_report'}.'*';
2648         $index{'auth_type'}    = "${auth_type_tag}${auth_type_sf}";
2649     }
2650     
2651     my %result;
2652     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2653     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2654     # limit to 10 char, should be enough, and limit the DB size
2655     $title = substr($title,0,10);
2656     #parse each field
2657     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2658     foreach my $field ($record->fields()) {
2659         #parse each subfield
2660         next if $field->tag <10;
2661         foreach my $subfield ($field->subfields()) {
2662             my $tag = $field->tag();
2663             my $subfieldcode = $subfield->[0];
2664             my $indexed=0;
2665             # check each index to see if the subfield is stored somewhere
2666             # otherwise, store it in __RAW__ index
2667             foreach my $key (keys %index) {
2668 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2669                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2670                     $indexed=1;
2671                     my $line= lc $subfield->[1];
2672                     # remove meaningless value in the field...
2673                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2674                     # ... and split in words
2675                     foreach (split / /,$line) {
2676                         next unless $_; # skip  empty values (multiple spaces)
2677                         # if the entry is already here, do nothing, the biblionumber has already be removed
2678                         unless ( defined( $result{$key}->{$_} ) && ( $result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) ) {
2679                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2680                             $sth2->execute($server,$key,$_);
2681                             my $existing_biblionumbers = $sth2->fetchrow;
2682                             # it exists
2683                             if ($existing_biblionumbers) {
2684 #                                 warn " existing for $key $_: $existing_biblionumbers";
2685                                 $result{$key}->{$_} =$existing_biblionumbers;
2686                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2687                             }
2688                         }
2689                     }
2690                 }
2691             }
2692             # the subfield is not indexed, store it in __RAW__ index anyway
2693             unless ($indexed) {
2694                 my $line= lc $subfield->[1];
2695                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2696                 # ... and split in words
2697                 foreach (split / /,$line) {
2698                     next unless $_; # skip  empty values (multiple spaces)
2699                     # if the entry is already here, do nothing, the biblionumber has already be removed
2700                     unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2701                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2702                         $sth2->execute($server,'__RAW__',$_);
2703                         my $existing_biblionumbers = $sth2->fetchrow;
2704                         # it exists
2705                         if ($existing_biblionumbers) {
2706                             $result{'__RAW__'}->{$_} =$existing_biblionumbers;
2707                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2708                         }
2709                     }
2710                 }
2711             }
2712         }
2713     }
2714     return %result;
2715 }
2716
2717 =head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
2718
2719     function to add a biblio in NoZebra indexes
2720
2721 =cut
2722
2723 sub _AddBiblioNoZebra {
2724     my ($biblionumber, $record, $server, %result)=@_;
2725     my $dbh = C4::Context->dbh;
2726     # Get the indexes
2727     my %index;
2728     my $title;
2729     if ($server eq 'biblioserver') {
2730         %index=GetNoZebraIndexes;
2731         # get title of the record (to store the 10 first letters with the index)
2732         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title', ''); # FIXME: should be GetFrameworkCode($biblionumber) ??
2733         $title = lc($record->subfield($titletag,$titlesubfield));
2734     } else {
2735         # warn "server : $server";
2736         # for authorities, the "title" is the $a mainentry
2737         my ($auth_type_tag, $auth_type_sf) = C4::AuthoritiesMarc::get_auth_type_location();
2738         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield($auth_type_tag, $auth_type_sf));
2739         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2740         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2741         $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
2742         $index{'mainentry'}     = $authref->{auth_tag_to_report}.'*';
2743         $index{'auth_type'}    = "${auth_type_tag}${auth_type_sf}";
2744     }
2745
2746     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2747     $title =~ s/ |\.|,|;|\[|\]|\(|\)|\*|-|'|:|=|\r|\n//g;
2748     # limit to 10 char, should be enough, and limit the DB size
2749     $title = substr($title,0,10);
2750     #parse each field
2751     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2752     foreach my $field ($record->fields()) {
2753         #parse each subfield
2754         ###FIXME: impossible to index a 001-009 value with NoZebra
2755         next if $field->tag <10;
2756         foreach my $subfield ($field->subfields()) {
2757             my $tag = $field->tag();
2758             my $subfieldcode = $subfield->[0];
2759             my $indexed=0;
2760 #             warn "INDEXING :".$subfield->[1];
2761             # check each index to see if the subfield is stored somewhere
2762             # otherwise, store it in __RAW__ index
2763             foreach my $key (keys %index) {
2764 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2765                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2766                     $indexed=1;
2767                     my $line= lc $subfield->[1];
2768                     # remove meaningless value in the field...
2769                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2770                     # ... and split in words
2771                     foreach (split / /,$line) {
2772                         next unless $_; # skip  empty values (multiple spaces)
2773                         # if the entry is already here, improve weight
2774 #                         warn "managing $_";
2775                         if ( exists $result{$key}->{$_} && $result{$key}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d+);/) {
2776                             my $weight = $1 + 1;
2777                             $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2778                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2779                         } else {
2780                             # get the value if it exist in the nozebra table, otherwise, create it
2781                             $sth2->execute($server,$key,$_);
2782                             my $existing_biblionumbers = $sth2->fetchrow;
2783                             # it exists
2784                             if ($existing_biblionumbers) {
2785                                 $result{$key}->{"$_"} =$existing_biblionumbers;
2786                                 my $weight = defined $1 ? $1 + 1 : 1;
2787                                 $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2788                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2789                             # create a new ligne for this entry
2790                             } else {
2791 #                             warn "INSERT : $server / $key / $_";
2792                                 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
2793                                 $result{$key}->{"$_"}.="$biblionumber,$title-1;";
2794                             }
2795                         }
2796                     }
2797                 }
2798             }
2799             # the subfield is not indexed, store it in __RAW__ index anyway
2800             unless ($indexed) {
2801                 my $line= lc $subfield->[1];
2802                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2803                 # ... and split in words
2804                 foreach (split / /,$line) {
2805                     next unless $_; # skip  empty values (multiple spaces)
2806                     # if the entry is already here, improve weight
2807                     my $tmpstr = $result{'__RAW__'}->{"$_"} || "";
2808                     if ($tmpstr =~ /$biblionumber,\Q$title\E\-(\d+);/) {
2809                         my $weight=$1+1;
2810                         $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2811                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2812                     } else {
2813                         # get the value if it exist in the nozebra table, otherwise, create it
2814                         $sth2->execute($server,'__RAW__',$_);
2815                         my $existing_biblionumbers = $sth2->fetchrow;
2816                         # it exists
2817                         if ($existing_biblionumbers) {
2818                             $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
2819                             my $weight = ($1 ? $1 : 0) + 1;
2820                             $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2821                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2822                         # create a new ligne for this entry
2823                         } else {
2824                             $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).',  indexname="__RAW__",value='.$dbh->quote($_));
2825                             $result{'__RAW__'}->{"$_"}.="$biblionumber,$title-1;";
2826                         }
2827                     }
2828                 }
2829             }
2830         }
2831     }
2832     return %result;
2833 }
2834
2835
2836 =head2 _find_value
2837
2838 =over 4
2839
2840 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2841
2842 Find the given $subfield in the given $tag in the given
2843 MARC::Record $record.  If the subfield is found, returns
2844 the (indicators, value) pair; otherwise, (undef, undef) is
2845 returned.
2846
2847 PROPOSITION :
2848 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2849 I suggest we export it from this module.
2850
2851 =back
2852
2853 =cut
2854
2855 sub _find_value {
2856     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2857     my @result;
2858     my $indicator;
2859     if ( $tagfield < 10 ) {
2860         if ( $record->field($tagfield) ) {
2861             push @result, $record->field($tagfield)->data();
2862         }
2863         else {
2864             push @result, "";
2865         }
2866     }
2867     else {
2868         foreach my $field ( $record->field($tagfield) ) {
2869             my @subfields = $field->subfields();
2870             foreach my $subfield (@subfields) {
2871                 if ( @$subfield[0] eq $insubfield ) {
2872                     push @result, @$subfield[1];
2873                     $indicator = $field->indicator(1) . $field->indicator(2);
2874                 }
2875             }
2876         }
2877     }
2878     return ( $indicator, @result );
2879 }
2880
2881 =head2 _koha_marc_update_bib_ids
2882
2883 =over 4
2884
2885 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
2886
2887 Internal function to add or update biblionumber and biblioitemnumber to
2888 the MARC XML.
2889
2890 =back
2891
2892 =cut
2893
2894 sub _koha_marc_update_bib_ids {
2895     my ($record, $frameworkcode, $biblionumber, $biblioitemnumber) = @_;
2896
2897     # we must add bibnum and bibitemnum in MARC::Record...
2898     # we build the new field with biblionumber and biblioitemnumber
2899     # we drop the original field
2900     # we add the new builded field.
2901     my ($biblio_tag, $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
2902     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
2903
2904     if ($biblio_tag != $biblioitem_tag) {
2905         # biblionumber & biblioitemnumber are in different fields
2906
2907         # deal with biblionumber
2908         my ($new_field, $old_field);
2909         if ($biblio_tag < 10) {
2910             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
2911         } else {
2912             $new_field =
2913               MARC::Field->new( $biblio_tag, '', '',
2914                 "$biblio_subfield" => $biblionumber );
2915         }
2916
2917         # drop old field and create new one...
2918         $old_field = $record->field($biblio_tag);
2919         $record->delete_field($old_field) if $old_field;
2920         $record->append_fields($new_field);
2921
2922         # deal with biblioitemnumber
2923         if ($biblioitem_tag < 10) {
2924             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
2925         } else {
2926             $new_field =
2927               MARC::Field->new( $biblioitem_tag, '', '',
2928                 "$biblioitem_subfield" => $biblioitemnumber, );
2929         }
2930         # drop old field and create new one...
2931         $old_field = $record->field($biblioitem_tag);
2932         $record->delete_field($old_field) if $old_field;
2933         $record->insert_fields_ordered($new_field);
2934
2935     } else {
2936         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
2937         my $new_field = MARC::Field->new(
2938             $biblio_tag, '', '',
2939             "$biblio_subfield" => $biblionumber,
2940             "$biblioitem_subfield" => $biblioitemnumber
2941         );
2942
2943         # drop old field and create new one...
2944         my $old_field = $record->field($biblio_tag);
2945         $record->delete_field($old_field) if $old_field;
2946         $record->insert_fields_ordered($new_field);
2947     }
2948 }
2949
2950 =head2 _koha_marc_update_biblioitem_cn_sort
2951
2952 =over 4
2953
2954 _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
2955
2956 =back
2957
2958 Given a MARC bib record and the biblioitem hash, update the
2959 subfield that contains a copy of the value of biblioitems.cn_sort.
2960
2961 =cut
2962
2963 sub _koha_marc_update_biblioitem_cn_sort {
2964     my $marc = shift;
2965     my $biblioitem = shift;
2966     my $frameworkcode= shift;
2967
2968     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.cn_sort",$frameworkcode);
2969     return unless $biblioitem_tag;
2970
2971     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2972
2973     if (my $field = $marc->field($biblioitem_tag)) {
2974         $field->delete_subfield(code => $biblioitem_subfield);
2975         if ($cn_sort ne '') {
2976             $field->add_subfields($biblioitem_subfield => $cn_sort);
2977         }
2978     } else {
2979         # if we get here, no biblioitem tag is present in the MARC record, so
2980         # we'll create it if $cn_sort is not empty -- this would be
2981         # an odd combination of events, however
2982         if ($cn_sort) {
2983             $marc->insert_grouped_field(MARC::Field->new($biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort));
2984         }
2985     }
2986 }
2987
2988 =head2 _koha_add_biblio
2989
2990 =over 4
2991
2992 my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
2993
2994 Internal function to add a biblio ($biblio is a hash with the values)
2995
2996 =back
2997
2998 =cut
2999
3000 sub _koha_add_biblio {
3001     my ( $dbh, $biblio, $frameworkcode ) = @_;
3002
3003     my $error;
3004
3005     # set the series flag
3006     my $serial = 0;
3007     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
3008
3009     my $query = 
3010         "INSERT INTO biblio
3011         SET frameworkcode = ?,
3012             author = ?,
3013             title = ?,
3014             unititle =?,
3015             notes = ?,
3016             serial = ?,
3017             seriestitle = ?,
3018             copyrightdate = ?,
3019             datecreated=NOW(),
3020             abstract = ?
3021         ";
3022     my $sth = $dbh->prepare($query);
3023     $sth->execute(
3024         $frameworkcode,
3025         $biblio->{'author'},
3026         $biblio->{'title'},
3027         $biblio->{'unititle'},
3028         $biblio->{'notes'},
3029         $serial,
3030         $biblio->{'seriestitle'},
3031         $biblio->{'copyrightdate'},
3032         $biblio->{'abstract'}
3033     );
3034
3035     my $biblionumber = $dbh->{'mysql_insertid'};
3036     if ( $dbh->errstr ) {
3037         $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
3038         warn $error;
3039     }
3040
3041     $sth->finish();
3042     #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3043     return ($biblionumber,$error);
3044 }
3045
3046 =head2 _koha_modify_biblio
3047
3048 =over 4
3049
3050 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3051
3052 Internal function for updating the biblio table
3053
3054 =back
3055
3056 =cut
3057
3058 sub _koha_modify_biblio {
3059     my ( $dbh, $biblio, $frameworkcode ) = @_;
3060     my $error;
3061
3062     my $query = "
3063         UPDATE biblio
3064         SET    frameworkcode = ?,
3065                author = ?,
3066                title = ?,
3067                unititle = ?,
3068                notes = ?,
3069                serial = ?,
3070                seriestitle = ?,
3071                copyrightdate = ?,
3072                abstract = ?
3073         WHERE  biblionumber = ?
3074         "
3075     ;
3076     my $sth = $dbh->prepare($query);
3077     
3078     $sth->execute(
3079         $frameworkcode,
3080         $biblio->{'author'},
3081         $biblio->{'title'},
3082         $biblio->{'unititle'},
3083         $biblio->{'notes'},
3084         $biblio->{'serial'},
3085         $biblio->{'seriestitle'},
3086         $biblio->{'copyrightdate'},
3087         $biblio->{'abstract'},
3088         $biblio->{'biblionumber'}
3089     ) if $biblio->{'biblionumber'};
3090
3091     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3092         $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
3093         warn $error;
3094     }
3095     return ( $biblio->{'biblionumber'},$error );
3096 }
3097
3098 =head2 _koha_modify_biblioitem_nonmarc
3099
3100 =over 4
3101
3102 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3103
3104 Updates biblioitems row except for marc and marcxml, which should be changed
3105 via ModBiblioMarc
3106
3107 =back
3108
3109 =cut
3110
3111 sub _koha_modify_biblioitem_nonmarc {
3112     my ( $dbh, $biblioitem ) = @_;
3113     my $error;
3114
3115     # re-calculate the cn_sort, it may have changed
3116     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3117
3118     my $query = 
3119     "UPDATE biblioitems 
3120     SET biblionumber    = ?,
3121         volume          = ?,
3122         number          = ?,
3123         itemtype        = ?,
3124         isbn            = ?,
3125         issn            = ?,
3126         publicationyear = ?,
3127         publishercode   = ?,
3128         volumedate      = ?,
3129         volumedesc      = ?,
3130         collectiontitle = ?,
3131         collectionissn  = ?,
3132         collectionvolume= ?,
3133         editionstatement= ?,
3134         editionresponsibility = ?,
3135         illus           = ?,
3136         pages           = ?,
3137         notes           = ?,
3138         size            = ?,
3139         place           = ?,
3140         lccn            = ?,
3141         url             = ?,
3142         cn_source       = ?,
3143         cn_class        = ?,
3144         cn_item         = ?,
3145         cn_suffix       = ?,
3146         cn_sort         = ?,
3147         totalissues     = ?
3148         where biblioitemnumber = ?
3149         ";
3150     my $sth = $dbh->prepare($query);
3151     $sth->execute(
3152         $biblioitem->{'biblionumber'},
3153         $biblioitem->{'volume'},
3154         $biblioitem->{'number'},
3155         $biblioitem->{'itemtype'},
3156         $biblioitem->{'isbn'},
3157         $biblioitem->{'issn'},
3158         $biblioitem->{'publicationyear'},
3159         $biblioitem->{'publishercode'},
3160         $biblioitem->{'volumedate'},
3161         $biblioitem->{'volumedesc'},
3162         $biblioitem->{'collectiontitle'},
3163         $biblioitem->{'collectionissn'},
3164         $biblioitem->{'collectionvolume'},
3165         $biblioitem->{'editionstatement'},
3166         $biblioitem->{'editionresponsibility'},
3167         $biblioitem->{'illus'},
3168         $biblioitem->{'pages'},
3169         $biblioitem->{'bnotes'},
3170         $biblioitem->{'size'},
3171         $biblioitem->{'place'},
3172         $biblioitem->{'lccn'},
3173         $biblioitem->{'url'},
3174         $biblioitem->{'biblioitems.cn_source'},
3175         $biblioitem->{'cn_class'},
3176         $biblioitem->{'cn_item'},
3177         $biblioitem->{'cn_suffix'},
3178         $cn_sort,
3179         $biblioitem->{'totalissues'},
3180         $biblioitem->{'biblioitemnumber'}
3181     );
3182     if ( $dbh->errstr ) {
3183         $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
3184         warn $error;
3185     }
3186     return ($biblioitem->{'biblioitemnumber'},$error);
3187 }
3188
3189 =head2 _koha_add_biblioitem
3190
3191 =over 4
3192
3193 my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3194
3195 Internal function to add a biblioitem
3196
3197 =back
3198
3199 =cut
3200
3201 sub _koha_add_biblioitem {
3202     my ( $dbh, $biblioitem ) = @_;
3203     my $error;
3204
3205     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3206     my $query =
3207     "INSERT INTO biblioitems SET
3208         biblionumber    = ?,
3209         volume          = ?,
3210         number          = ?,
3211         itemtype        = ?,
3212         isbn            = ?,
3213         issn            = ?,
3214         publicationyear = ?,
3215         publishercode   = ?,
3216         volumedate      = ?,
3217         volumedesc      = ?,
3218         collectiontitle = ?,
3219         collectionissn  = ?,
3220         collectionvolume= ?,
3221         editionstatement= ?,
3222         editionresponsibility = ?,
3223         illus           = ?,
3224         pages           = ?,
3225         notes           = ?,
3226         size            = ?,
3227         place           = ?,
3228         lccn            = ?,
3229         marc            = ?,
3230         url             = ?,
3231         cn_source       = ?,
3232         cn_class        = ?,
3233         cn_item         = ?,
3234         cn_suffix       = ?,
3235         cn_sort         = ?,
3236         totalissues     = ?
3237         ";
3238     my $sth = $dbh->prepare($query);
3239     $sth->execute(
3240         $biblioitem->{'biblionumber'},
3241         $biblioitem->{'volume'},
3242         $biblioitem->{'number'},
3243         $biblioitem->{'itemtype'},
3244         $biblioitem->{'isbn'},
3245         $biblioitem->{'issn'},
3246         $biblioitem->{'publicationyear'},
3247         $biblioitem->{'publishercode'},
3248         $biblioitem->{'volumedate'},
3249         $biblioitem->{'volumedesc'},
3250         $biblioitem->{'collectiontitle'},
3251         $biblioitem->{'collectionissn'},
3252         $biblioitem->{'collectionvolume'},
3253         $biblioitem->{'editionstatement'},
3254         $biblioitem->{'editionresponsibility'},
3255         $biblioitem->{'illus'},
3256         $biblioitem->{'pages'},
3257         $biblioitem->{'bnotes'},
3258         $biblioitem->{'size'},
3259         $biblioitem->{'place'},
3260         $biblioitem->{'lccn'},
3261         $biblioitem->{'marc'},
3262         $biblioitem->{'url'},
3263         $biblioitem->{'biblioitems.cn_source'},
3264         $biblioitem->{'cn_class'},
3265         $biblioitem->{'cn_item'},
3266         $biblioitem->{'cn_suffix'},
3267         $cn_sort,
3268         $biblioitem->{'totalissues'}
3269     );
3270     my $bibitemnum = $dbh->{'mysql_insertid'};
3271     if ( $dbh->errstr ) {
3272         $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
3273         warn $error;
3274     }
3275     $sth->finish();
3276     return ($bibitemnum,$error);
3277 }
3278
3279 =head2 _koha_delete_biblio
3280
3281 =over 4
3282
3283 $error = _koha_delete_biblio($dbh,$biblionumber);
3284
3285 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3286
3287 C<$dbh> - the database handle
3288 C<$biblionumber> - the biblionumber of the biblio to be deleted
3289
3290 =back
3291
3292 =cut
3293
3294 # FIXME: add error handling
3295
3296 sub _koha_delete_biblio {
3297     my ( $dbh, $biblionumber ) = @_;
3298
3299     # get all the data for this biblio
3300     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3301     $sth->execute($biblionumber);
3302
3303     if ( my $data = $sth->fetchrow_hashref ) {
3304
3305         # save the record in deletedbiblio
3306         # find the fields to save
3307         my $query = "INSERT INTO deletedbiblio SET ";
3308         my @bind  = ();
3309         foreach my $temp ( keys %$data ) {
3310             $query .= "$temp = ?,";
3311             push( @bind, $data->{$temp} );
3312         }
3313
3314         # replace the last , by ",?)"
3315         $query =~ s/\,$//;
3316         my $bkup_sth = $dbh->prepare($query);
3317         $bkup_sth->execute(@bind);
3318         $bkup_sth->finish;
3319
3320         # delete the biblio
3321         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3322         $del_sth->execute($biblionumber);
3323         $del_sth->finish;
3324     }
3325     $sth->finish;
3326     return undef;
3327 }
3328
3329 =head2 _koha_delete_biblioitems
3330
3331 =over 4
3332
3333 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3334
3335 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3336
3337 C<$dbh> - the database handle
3338 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3339
3340 =back
3341
3342 =cut
3343
3344 # FIXME: add error handling
3345
3346 sub _koha_delete_biblioitems {
3347     my ( $dbh, $biblioitemnumber ) = @_;
3348
3349     # get all the data for this biblioitem
3350     my $sth =
3351       $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3352     $sth->execute($biblioitemnumber);
3353
3354     if ( my $data = $sth->fetchrow_hashref ) {
3355
3356         # save the record in deletedbiblioitems
3357         # find the fields to save
3358         my $query = "INSERT INTO deletedbiblioitems SET ";
3359         my @bind  = ();
3360         foreach my $temp ( keys %$data ) {
3361             $query .= "$temp = ?,";
3362             push( @bind, $data->{$temp} );
3363         }
3364
3365         # replace the last , by ",?)"
3366         $query =~ s/\,$//;
3367         my $bkup_sth = $dbh->prepare($query);
3368         $bkup_sth->execute(@bind);
3369         $bkup_sth->finish;
3370
3371         # delete the biblioitem
3372         my $del_sth =
3373           $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3374         $del_sth->execute($biblioitemnumber);
3375         $del_sth->finish;
3376     }
3377     $sth->finish;
3378     return undef;
3379 }
3380
3381 =head1 UNEXPORTED FUNCTIONS
3382
3383 =head2 ModBiblioMarc
3384
3385     &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3386     
3387     Add MARC data for a biblio to koha 
3388     
3389     Function exported, but should NOT be used, unless you really know what you're doing
3390
3391 =cut
3392
3393 sub ModBiblioMarc {
3394     
3395 # pass the MARC::Record to this function, and it will create the records in the marc field
3396     my ( $record, $biblionumber, $frameworkcode ) = @_;
3397     my $dbh = C4::Context->dbh;
3398     my @fields = $record->fields();
3399     if ( !$frameworkcode ) {
3400         $frameworkcode = "";
3401     }
3402     my $sth =
3403       $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3404     $sth->execute( $frameworkcode, $biblionumber );
3405     $sth->finish;
3406     my $encoding = C4::Context->preference("marcflavour");
3407
3408     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3409     if ( $encoding eq "UNIMARC" ) {
3410         my $string = $record->subfield( 100, "a" );
3411         if ( ($string) && ( length($record->subfield( 100, "a" )) == 35 ) ) {
3412             my $f100 = $record->field(100);
3413             $record->delete_field($f100);
3414         }
3415         else {
3416             $string = POSIX::strftime( "%Y%m%d", localtime );
3417             $string =~ s/\-//g;
3418             $string = sprintf( "%-*s", 35, $string );
3419         }
3420         substr( $string, 22, 6, "frey50" );
3421         unless ( $record->subfield( 100, "a" ) ) {
3422             $record->insert_grouped_field(
3423                 MARC::Field->new( 100, "", "", "a" => $string ) );
3424         }
3425     }
3426     my $oldRecord;
3427     if (C4::Context->preference("NoZebra")) {
3428         # only NoZebra indexing needs to have
3429         # the previous version of the record
3430         $oldRecord = GetMarcBiblio($biblionumber);
3431     }
3432     $sth =
3433       $dbh->prepare(
3434         "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
3435     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
3436         $biblionumber );
3437     $sth->finish;
3438     ModZebra($biblionumber,"specialUpdate","biblioserver",$oldRecord,$record);
3439     return $biblionumber;
3440 }
3441
3442 =head2 z3950_extended_services
3443
3444 z3950_extended_services($serviceType,$serviceOptions,$record);
3445
3446     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.
3447
3448 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
3449
3450 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
3451
3452     action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
3453
3454 and maybe
3455
3456     recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
3457     syntax => the record syntax (transfer syntax)
3458     databaseName = Database from connection object
3459
3460     To set serviceOptions, call set_service_options($serviceType)
3461
3462 C<$record> the record, if one is needed for the service type
3463
3464     A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
3465
3466 =cut
3467
3468 sub z3950_extended_services {
3469     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
3470
3471     # get our connection object
3472     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
3473
3474     # create a new package object
3475     my $Zpackage = $Zconn->package();
3476
3477     # set our options
3478     $Zpackage->option( action => $action );
3479
3480     if ( $serviceOptions->{'databaseName'} ) {
3481         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
3482     }
3483     if ( $serviceOptions->{'recordIdNumber'} ) {
3484         $Zpackage->option(
3485             recordIdNumber => $serviceOptions->{'recordIdNumber'} );
3486     }
3487     if ( $serviceOptions->{'recordIdOpaque'} ) {
3488         $Zpackage->option(
3489             recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
3490     }
3491
3492  # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
3493  #if ($serviceType eq 'itemorder') {
3494  #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
3495  #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
3496  #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
3497  #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
3498  #}
3499
3500     if ( $serviceOptions->{record} ) {
3501         $Zpackage->option( record => $serviceOptions->{record} );
3502
3503         # can be xml or marc
3504         if ( $serviceOptions->{'syntax'} ) {
3505             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
3506         }
3507     }
3508
3509     # send the request, handle any exception encountered
3510     eval { $Zpackage->send($serviceType) };
3511     if ( $@ && $@->isa("ZOOM::Exception") ) {
3512         return "error:  " . $@->code() . " " . $@->message() . "\n";
3513     }
3514
3515     # free up package resources
3516     $Zpackage->destroy();
3517 }
3518
3519 =head2 set_service_options
3520
3521 my $serviceOptions = set_service_options($serviceType);
3522
3523 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
3524
3525 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
3526
3527 =cut
3528
3529 sub set_service_options {
3530     my ($serviceType) = @_;
3531     my $serviceOptions;
3532
3533 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
3534 #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
3535
3536     if ( $serviceType eq 'commit' ) {
3537
3538         # nothing to do
3539     }
3540     if ( $serviceType eq 'create' ) {
3541
3542         # nothing to do
3543     }
3544     if ( $serviceType eq 'drop' ) {
3545         die "ERROR: 'drop' not currently supported (by Zebra)";
3546     }
3547     return $serviceOptions;
3548 }
3549
3550 =head3 get_biblio_authorised_values
3551
3552   find the types and values for all authorised values assigned to this biblio.
3553
3554   parameters:
3555     biblionumber
3556     MARC::Record of the bib
3557
3558   returns: a hashref mapping the authorised value to the value set for this biblionumber
3559
3560       $authorised_values = {
3561                              'Scent'     => 'flowery',
3562                              'Audience'  => 'Young Adult',
3563                              'itemtypes' => 'SER',
3564                            };
3565
3566   Notes: forlibrarian should probably be passed in, and called something different.
3567
3568
3569 =cut
3570
3571 sub get_biblio_authorised_values {
3572     my $biblionumber = shift;
3573     my $record       = shift;
3574     
3575     my $forlibrarian = 1; # are we in staff or opac?
3576     my $frameworkcode = GetFrameworkCode( $biblionumber );
3577
3578     my $authorised_values;
3579
3580     my $tagslib = GetMarcStructure( $forlibrarian, $frameworkcode )
3581       or return $authorised_values;
3582
3583     # assume that these entries in the authorised_value table are bibliolevel.
3584     # ones that start with 'item%' are item level.
3585     my $query = q(SELECT distinct authorised_value, kohafield
3586                     FROM marc_subfield_structure
3587                     WHERE authorised_value !=''
3588                       AND (kohafield like 'biblio%'
3589                        OR  kohafield like '') );
3590     my $bibliolevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
3591     
3592     foreach my $tag ( keys( %$tagslib ) ) {
3593         foreach my $subfield ( keys( %{$tagslib->{ $tag }} ) ) {
3594             # warn "checking $subfield. type is: " . ref $tagslib->{ $tag }{ $subfield };
3595             if ( 'HASH' eq ref $tagslib->{ $tag }{ $subfield } ) {
3596                 if ( defined $tagslib->{ $tag }{ $subfield }{'authorised_value'} && exists $bibliolevel_authorised_values->{ $tagslib->{ $tag }{ $subfield }{'authorised_value'} } ) {
3597                     if ( defined $record->field( $tag ) ) {
3598                         my $this_subfield_value = $record->field( $tag )->subfield( $subfield );
3599                         if ( defined $this_subfield_value ) {
3600                             $authorised_values->{ $tagslib->{ $tag }{ $subfield }{'authorised_value'} } = $this_subfield_value;
3601                         }
3602                     }
3603                 }
3604             }
3605         }
3606     }
3607     # warn ( Data::Dumper->Dump( [ $authorised_values ], [ 'authorised_values' ] ) );
3608     return $authorised_values;
3609 }
3610
3611
3612 1;
3613
3614 __END__
3615
3616 =head1 AUTHOR
3617
3618 Koha Developement team <info@koha.org>
3619
3620 Paul POULAIN paul.poulain@free.fr
3621
3622 Joshua Ferraro jmf@liblime.com
3623
3624 =cut