TransformHTMLToXML bug default_ind_to_space
[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         @$indicator=map{sprintf("%2s",$_) unless ( length($_)<2)}@$indicator;
1773     for ( my $i = 0 ; $i < @$tags ; $i++ ) {
1774         if (C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a") {
1775             # if we have a 100 field and it's values are not correct, skip them.
1776             # if we don't have any valid 100 field, we will create a default one at the end
1777             my $enc = substr( @$values[$i], 26, 2 );
1778             if ($enc eq '01' or $enc eq '50' or $enc eq '03') {
1779                 $unimarc_and_100_exist=1;
1780             } else {
1781                 next;
1782             }
1783         }
1784         @$values[$i] =~ s/&/&amp;/g;
1785         @$values[$i] =~ s/</&lt;/g;
1786         @$values[$i] =~ s/>/&gt;/g;
1787         @$values[$i] =~ s/"/&quot;/g;
1788         @$values[$i] =~ s/'/&apos;/g;
1789 #         if ( !utf8::is_utf8( @$values[$i] ) ) {
1790 #             utf8::decode( @$values[$i] );
1791 #         }
1792         if ( ( @$tags[$i] ne $prevtag ) ) {
1793             $j++ unless ( @$tags[$i] eq "" );
1794             if ( !$first ) {
1795                 $xml .= "</datafield>\n";
1796                 if (   ( @$tags[$i] && @$tags[$i] > 10 )
1797                     && ( @$values[$i] ne "" ) )
1798                 {
1799                     my $ind1 = _default_ind_to_space(substr( @$indicator[$j], 0, 1 ));
1800                     my $ind2;
1801                     if ( @$indicator[$j] ) {
1802                         $ind2 = _default_ind_to_space(substr( @$indicator[$j], 1, 1 ));
1803                     }
1804                     else {
1805                         warn "Indicator in @$tags[$i] is empty";
1806                         $ind2 = " ";
1807                     }
1808                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1809                     $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1810                     $first = 0;
1811                 }
1812                 else {
1813                     $first = 1;
1814                 }
1815             }
1816             else {
1817                 if ( @$values[$i] ne "" ) {
1818
1819                     # leader
1820                     if ( @$tags[$i] eq "000" ) {
1821                         $xml .= "<leader>@$values[$i]</leader>\n";
1822                         $first = 1;
1823
1824                         # rest of the fixed fields
1825                     }
1826                     elsif ( @$tags[$i] < 10 ) {
1827                         $xml .= "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
1828                         $first = 1;
1829                     }
1830                     else {
1831                         my $ind1 = _default_ind_to_space( substr( @$indicator[$j], 0, 1 ) );
1832                         my $ind2 = _default_ind_to_space( substr( @$indicator[$j], 1, 1 ) );
1833                         $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1834                         $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1835                         $first = 0;
1836                     }
1837                 }
1838             }
1839         }
1840         else {    # @$tags[$i] eq $prevtag
1841             if ( @$values[$i] eq "" ) {
1842             }
1843             else {
1844                 if ($first) {
1845                     my $ind1 = _default_ind_to_space( substr( @$indicator[$j], 0, 1 ) );
1846                     my $ind2 = _default_ind_to_space( substr( @$indicator[$j], 1, 1 ) );
1847                     $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
1848                     $first = 0;
1849                 }
1850                 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
1851             }
1852         }
1853         $prevtag = @$tags[$i];
1854     }
1855     $xml .= "</datafield>\n" if @$tags > 0;
1856     if (C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist) {
1857 #     warn "SETTING 100 for $auth_type";
1858         my $string = strftime( "%Y%m%d", localtime(time) );
1859         # set 50 to position 26 is biblios, 13 if authorities
1860         my $pos=26;
1861         $pos=13 if $auth_type eq 'UNIMARCAUTH';
1862         $string = sprintf( "%-*s", 35, $string );
1863         substr( $string, $pos , 6, "50" );
1864         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
1865         $xml .= "<subfield code=\"a\">$string</subfield>\n";
1866         $xml .= "</datafield>\n";
1867     }
1868     $xml .= "</record>\n";
1869     $xml .= MARC::File::XML::footer();
1870     return $xml;
1871 }
1872
1873 =head2 _default_ind_to_space
1874
1875 Passed what should be an indicator returns a space
1876 if its undefined or zero length
1877
1878 =cut
1879
1880 sub _default_ind_to_space {
1881     my $s = shift;
1882     if (!defined $s || $s eq q{}) {
1883         return ' ';
1884     }
1885     return $s;
1886 }
1887
1888 =head2 TransformHtmlToMarc
1889
1890     L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
1891     L<$params> is a ref to an array as below:
1892     {
1893         'tag_010_indicator1_531951' ,
1894         'tag_010_indicator2_531951' ,
1895         'tag_010_code_a_531951_145735' ,
1896         'tag_010_subfield_a_531951_145735' ,
1897         'tag_200_indicator1_873510' ,
1898         'tag_200_indicator2_873510' ,
1899         'tag_200_code_a_873510_673465' ,
1900         'tag_200_subfield_a_873510_673465' ,
1901         'tag_200_code_b_873510_704318' ,
1902         'tag_200_subfield_b_873510_704318' ,
1903         'tag_200_code_e_873510_280822' ,
1904         'tag_200_subfield_e_873510_280822' ,
1905         'tag_200_code_f_873510_110730' ,
1906         'tag_200_subfield_f_873510_110730' ,
1907     }
1908     L<$cgi> is the CGI object which containts the value.
1909     L<$record> is the MARC::Record object.
1910
1911 =cut
1912
1913 sub TransformHtmlToMarc {
1914     my $params = shift;
1915     my $cgi    = shift;
1916
1917     # explicitly turn on the UTF-8 flag for all
1918     # 'tag_' parameters to avoid incorrect character
1919     # conversion later on
1920     my $cgi_params = $cgi->Vars;
1921     foreach my $param_name (keys %$cgi_params) {
1922         if ($param_name =~ /^tag_/) {
1923             my $param_value = $cgi_params->{$param_name};
1924             if (utf8::decode($param_value)) {
1925                 $cgi_params->{$param_name} = $param_value;
1926             } 
1927             # FIXME - need to do something if string is not valid UTF-8
1928         }
1929     }
1930    
1931     # creating a new record
1932     my $record  = MARC::Record->new();
1933     my $i=0;
1934     my @fields;
1935     while ($params->[$i]){ # browse all CGI params
1936         my $param = $params->[$i];
1937         my $newfield=0;
1938         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
1939         if ($param eq 'biblionumber') {
1940             my ( $biblionumbertagfield, $biblionumbertagsubfield ) =
1941                 &GetMarcFromKohaField( "biblio.biblionumber", '' );
1942             if ($biblionumbertagfield < 10) {
1943                 $newfield = MARC::Field->new(
1944                     $biblionumbertagfield,
1945                     $cgi->param($param),
1946                 );
1947             } else {
1948                 $newfield = MARC::Field->new(
1949                     $biblionumbertagfield,
1950                     '',
1951                     '',
1952                     "$biblionumbertagsubfield" => $cgi->param($param),
1953                 );
1954             }
1955             push @fields,$newfield if($newfield);
1956         } 
1957         elsif ($param =~ /^tag_(\d*)_indicator1_/){ # new field start when having 'input name="..._indicator1_..."
1958             my $tag  = $1;
1959             
1960             my $ind1 = _default_ind_to_space(substr($cgi->param($param),          0, 1));
1961             my $ind2 = _default_ind_to_space(substr($cgi->param($params->[$i+1]), 0, 1));
1962             $newfield=0;
1963             my $j=$i+2;
1964             
1965             if($tag < 10){ # no code for theses fields
1966     # in MARC editor, 000 contains the leader.
1967                 if ($tag eq '000' ) {
1968                     $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==24;
1969     # between 001 and 009 (included)
1970                 } elsif ($cgi->param($params->[$j+1]) ne '') {
1971                     $newfield = MARC::Field->new(
1972                         $tag,
1973                         $cgi->param($params->[$j+1]),
1974                     );
1975                 }
1976     # > 009, deal with subfields
1977             } else {
1978                 while(defined $params->[$j] && $params->[$j] =~ /_code_/){ # browse all it's subfield
1979                     my $inner_param = $params->[$j];
1980                     if ($newfield){
1981                         if($cgi->param($params->[$j+1]) ne ''){  # only if there is a value (code => value)
1982                             $newfield->add_subfields(
1983                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
1984                             );
1985                         }
1986                     } else {
1987                         if ( $cgi->param($params->[$j+1]) ne '' ) { # creating only if there is a value (code => value)
1988                             $newfield = MARC::Field->new(
1989                                 $tag,
1990                                 $ind1,
1991                                 $ind2,
1992                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1]),
1993                             );
1994                         }
1995                     }
1996                     $j+=2;
1997                 }
1998             }
1999             push @fields,$newfield if($newfield);
2000         }
2001         $i++;
2002     }
2003     
2004     $record->append_fields(@fields);
2005     return $record;
2006 }
2007
2008 # cache inverted MARC field map
2009 our $inverted_field_map;
2010
2011 =head2 TransformMarcToKoha
2012
2013 =over 4
2014
2015     $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2016
2017 =back
2018
2019 Extract data from a MARC bib record into a hashref representing
2020 Koha biblio, biblioitems, and items fields. 
2021
2022 =cut
2023 sub TransformMarcToKoha {
2024     my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
2025
2026     my $result;
2027     $limit_table=$limit_table||0;
2028     $frameworkcode = '' unless defined $frameworkcode;
2029     
2030     unless (defined $inverted_field_map) {
2031         $inverted_field_map = _get_inverted_marc_field_map();
2032     }
2033
2034     my %tables = ();
2035     if ( defined $limit_table && $limit_table eq 'items') {
2036         $tables{'items'} = 1;
2037     } else {
2038         $tables{'items'} = 1;
2039         $tables{'biblio'} = 1;
2040         $tables{'biblioitems'} = 1;
2041     }
2042
2043     # traverse through record
2044     MARCFIELD: foreach my $field ($record->fields()) {
2045         my $tag = $field->tag();
2046         next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
2047         if ($field->is_control_field()) {
2048             my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
2049             ENTRY: foreach my $entry (@{ $kohafields }) {
2050                 my ($subfield, $table, $column) = @{ $entry };
2051                 next ENTRY unless exists $tables{$table};
2052                 my $key = _disambiguate($table, $column);
2053                 if ($result->{$key}) {
2054                     unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($field->data() eq "")) {
2055                         $result->{$key} .= " | " . $field->data();
2056                     }
2057                 } else {
2058                     $result->{$key} = $field->data();
2059                 }
2060             }
2061         } else {
2062             # deal with subfields
2063             MARCSUBFIELD: foreach my $sf ($field->subfields()) {
2064                 my $code = $sf->[0];
2065                 next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
2066                 my $value = $sf->[1];
2067                 SFENTRY: foreach my $entry (@{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} }) {
2068                     my ($table, $column) = @{ $entry };
2069                     next SFENTRY unless exists $tables{$table};
2070                     my $key = _disambiguate($table, $column);
2071                     if ($result->{$key}) {
2072                         unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
2073                             $result->{$key} .= " | " . $value;
2074                         }
2075                     } else {
2076                         $result->{$key} = $value;
2077                     }
2078                 }
2079             }
2080         }
2081     }
2082
2083     # modify copyrightdate to keep only the 1st year found
2084     if (exists $result->{'copyrightdate'}) {
2085         my $temp = $result->{'copyrightdate'};
2086         $temp =~ m/c(\d\d\d\d)/;
2087         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
2088             $result->{'copyrightdate'} = $1;
2089         }
2090         else {                      # if no cYYYY, get the 1st date.
2091             $temp =~ m/(\d\d\d\d)/;
2092             $result->{'copyrightdate'} = $1;
2093         }
2094     }
2095
2096     # modify publicationyear to keep only the 1st year found
2097     if (exists $result->{'publicationyear'}) {
2098         my $temp = $result->{'publicationyear'};
2099         if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
2100             $result->{'publicationyear'} = $1;
2101         }
2102         else {                      # if no cYYYY, get the 1st date.
2103             $temp =~ m/(\d\d\d\d)/;
2104             $result->{'publicationyear'} = $1;
2105         }
2106     }
2107
2108     return $result;
2109 }
2110
2111 sub _get_inverted_marc_field_map {
2112     my $field_map = {};
2113     my $relations = C4::Context->marcfromkohafield;
2114
2115     foreach my $frameworkcode (keys %{ $relations }) {
2116         foreach my $kohafield (keys %{ $relations->{$frameworkcode} }) {
2117             next unless @{ $relations->{$frameworkcode}->{$kohafield} }; # not all columns are mapped to MARC tag & subfield
2118             my $tag = $relations->{$frameworkcode}->{$kohafield}->[0];
2119             my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
2120             my ($table, $column) = split /[.]/, $kohafield, 2;
2121             push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
2122             push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
2123         }
2124     }
2125     return $field_map;
2126 }
2127
2128 =head2 _disambiguate
2129
2130 =over 4
2131
2132 $newkey = _disambiguate($table, $field);
2133
2134 This is a temporary hack to distinguish between the
2135 following sets of columns when using TransformMarcToKoha.
2136
2137 items.cn_source & biblioitems.cn_source
2138 items.cn_sort & biblioitems.cn_sort
2139
2140 Columns that are currently NOT distinguished (FIXME
2141 due to lack of time to fully test) are:
2142
2143 biblio.notes and biblioitems.notes
2144 biblionumber
2145 timestamp
2146 biblioitemnumber
2147
2148 FIXME - this is necessary because prefixing each column
2149 name with the table name would require changing lots
2150 of code and templates, and exposing more of the DB
2151 structure than is good to the UI templates, particularly
2152 since biblio and bibloitems may well merge in a future
2153 version.  In the future, it would also be good to 
2154 separate DB access and UI presentation field names
2155 more.
2156
2157 =back
2158
2159 =cut
2160
2161 sub CountItemsIssued {
2162   my ( $biblionumber )  = @_;
2163   my $dbh = C4::Context->dbh;
2164   my $sth = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
2165   $sth->execute( $biblionumber );
2166   my $row = $sth->fetchrow_hashref();
2167   return $row->{'issuedCount'};
2168 }
2169
2170 sub _disambiguate {
2171     my ($table, $column) = @_;
2172     if ($column eq "cn_sort" or $column eq "cn_source") {
2173         return $table . '.' . $column;
2174     } else {
2175         return $column;
2176     }
2177
2178 }
2179
2180 =head2 get_koha_field_from_marc
2181
2182 =over 4
2183
2184 $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2185
2186 Internal function to map data from the MARC record to a specific non-MARC field.
2187 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2188
2189 =back
2190
2191 =cut
2192
2193 sub get_koha_field_from_marc {
2194     my ($koha_table,$koha_column,$record,$frameworkcode) = @_;
2195     my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table.'.'.$koha_column, $frameworkcode );  
2196     my $kohafield;
2197     foreach my $field ( $record->field($tagfield) ) {
2198         if ( $field->tag() < 10 ) {
2199             if ( $kohafield ) {
2200                 $kohafield .= " | " . $field->data();
2201             }
2202             else {
2203                 $kohafield = $field->data();
2204             }
2205         }
2206         else {
2207             if ( $field->subfields ) {
2208                 my @subfields = $field->subfields();
2209                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2210                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2211                         if ( $kohafield ) {
2212                             $kohafield .=
2213                               " | " . $subfields[$subfieldcount][1];
2214                         }
2215                         else {
2216                             $kohafield =
2217                               $subfields[$subfieldcount][1];
2218                         }
2219                     }
2220                 }
2221             }
2222         }
2223     }
2224     return $kohafield;
2225
2226
2227
2228 =head2 TransformMarcToKohaOneField
2229
2230 =over 4
2231
2232 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2233
2234 =back
2235
2236 =cut
2237
2238 sub TransformMarcToKohaOneField {
2239
2240     # FIXME ? if a field has a repeatable subfield that is used in old-db,
2241     # only the 1st will be retrieved...
2242     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2243     my $res = "";
2244     my ( $tagfield, $subfield ) =
2245       GetMarcFromKohaField( $kohatable . "." . $kohafield,
2246         $frameworkcode );
2247     foreach my $field ( $record->field($tagfield) ) {
2248         if ( $field->tag() < 10 ) {
2249             if ( $result->{$kohafield} ) {
2250                 $result->{$kohafield} .= " | " . $field->data();
2251             }
2252             else {
2253                 $result->{$kohafield} = $field->data();
2254             }
2255         }
2256         else {
2257             if ( $field->subfields ) {
2258                 my @subfields = $field->subfields();
2259                 foreach my $subfieldcount ( 0 .. $#subfields ) {
2260                     if ( $subfields[$subfieldcount][0] eq $subfield ) {
2261                         if ( $result->{$kohafield} ) {
2262                             $result->{$kohafield} .=
2263                               " | " . $subfields[$subfieldcount][1];
2264                         }
2265                         else {
2266                             $result->{$kohafield} =
2267                               $subfields[$subfieldcount][1];
2268                         }
2269                     }
2270                 }
2271             }
2272         }
2273     }
2274     return $result;
2275 }
2276
2277 =head1  OTHER FUNCTIONS
2278
2279
2280 =head2 PrepareItemrecordDisplay
2281
2282 =over 4
2283
2284 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
2285
2286 Returns a hash with all the fields for Display a given item data in a template
2287
2288 The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
2289
2290 =back
2291
2292 =cut
2293
2294 sub PrepareItemrecordDisplay {
2295
2296     my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2297
2298     my $dbh = C4::Context->dbh;
2299     $frameworkcode = &GetFrameworkCode( $bibnum ) if $bibnum;
2300     my ( $itemtagfield, $itemtagsubfield ) =
2301       &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2302     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2303     # return nothing if we don't have found an existing framework.
2304     return "" unless $tagslib;
2305     my $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum) if ($itemnum);
2306     my @loop_data;
2307     my $authorised_values_sth =
2308       $dbh->prepare(
2309 "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"
2310       );
2311     foreach my $tag ( sort keys %{$tagslib} ) {
2312         my $previous_tag = '';
2313         if ( $tag ne '' ) {
2314             # loop through each subfield
2315             my $cntsubf;
2316             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2317                 next if ( subfield_is_koha_internal_p($subfield) );
2318                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2319                 my %subfield_data;
2320                 $subfield_data{tag}           = $tag;
2321                 $subfield_data{subfield}      = $subfield;
2322                 $subfield_data{countsubfield} = $cntsubf++;
2323                 $subfield_data{kohafield}     =
2324                   $tagslib->{$tag}->{$subfield}->{'kohafield'};
2325
2326          #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2327                 $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib};
2328                 $subfield_data{mandatory} =
2329                   $tagslib->{$tag}->{$subfield}->{mandatory};
2330                 $subfield_data{repeatable} =
2331                   $tagslib->{$tag}->{$subfield}->{repeatable};
2332                 $subfield_data{hidden} = "display:none"
2333                   if $tagslib->{$tag}->{$subfield}->{hidden};
2334                   my ( $x, $value );
2335                   if ($itemrecord) {
2336                       ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord );
2337                   }
2338                   if (!defined $value) {
2339                       $value = q||;
2340                   }
2341                   $value =~ s/"/&quot;/g;
2342
2343                 # search for itemcallnumber if applicable
2344                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2345                     'items.itemcallnumber'
2346                     && C4::Context->preference('itemcallnumber') )
2347                 {
2348                     my $CNtag =
2349                       substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2350                     my $CNsubfield =
2351                       substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2352                     my $temp = $itemrecord->field($CNtag) if ($itemrecord);
2353                     if ($temp) {
2354                         $value = $temp->subfield($CNsubfield);
2355                     }
2356                 }
2357                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
2358                     'items.itemcallnumber'
2359                     && $defaultvalues && $defaultvalues->{'callnumber'} )
2360                 {
2361                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2362                     unless ($temp) {
2363                         $value = $defaultvalues->{'callnumber'} if $defaultvalues;
2364                     }
2365                 }
2366                 if ( ($tagslib->{$tag}->{$subfield}->{kohafield} eq
2367                     'items.holdingbranch' ||
2368                     $tagslib->{$tag}->{$subfield}->{kohafield} eq
2369                     'items.homebranch')          
2370                     && $defaultvalues && $defaultvalues->{'branchcode'} )
2371                 {
2372                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2373                     unless ($temp) {
2374                         $value = $defaultvalues->{branchcode}  if $defaultvalues;
2375                     }
2376                 }
2377                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2378                     my @authorised_values;
2379                     my %authorised_lib;
2380
2381                     # builds list, depending on authorised value...
2382                     #---- branch
2383                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
2384                         "branches" )
2385                     {
2386                         if ( ( C4::Context->preference("IndependantBranches") )
2387                             && ( C4::Context->userenv->{flags} % 2 != 1 ) )
2388                         {
2389                             my $sth =
2390                               $dbh->prepare(
2391                                 "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname"
2392                               );
2393                             $sth->execute( C4::Context->userenv->{branch} );
2394                             push @authorised_values, ""
2395                               unless (
2396                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2397                             while ( my ( $branchcode, $branchname ) =
2398                                 $sth->fetchrow_array )
2399                             {
2400                                 push @authorised_values, $branchcode;
2401                                 $authorised_lib{$branchcode} = $branchname;
2402                             }
2403                         }
2404                         else {
2405                             my $sth =
2406                               $dbh->prepare(
2407                                 "SELECT branchcode,branchname FROM branches ORDER BY branchname"
2408                               );
2409                             $sth->execute;
2410                             push @authorised_values, ""
2411                               unless (
2412                                 $tagslib->{$tag}->{$subfield}->{mandatory} );
2413                             while ( my ( $branchcode, $branchname ) =
2414                                 $sth->fetchrow_array )
2415                             {
2416                                 push @authorised_values, $branchcode;
2417                                 $authorised_lib{$branchcode} = $branchname;
2418                             }
2419                         }
2420
2421                         #----- itemtypes
2422                     }
2423                     elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
2424                         "itemtypes" )
2425                     {
2426                         my $sth =
2427                           $dbh->prepare(
2428                             "SELECT itemtype,description FROM itemtypes ORDER BY description"
2429                           );
2430                         $sth->execute;
2431                         push @authorised_values, ""
2432                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2433                         while ( my ( $itemtype, $description ) =
2434                             $sth->fetchrow_array )
2435                         {
2436                             push @authorised_values, $itemtype;
2437                             $authorised_lib{$itemtype} = $description;
2438                         }
2439
2440                         #---- "true" authorised value
2441                     }
2442                     else {
2443                         $authorised_values_sth->execute(
2444                             $tagslib->{$tag}->{$subfield}->{authorised_value} );
2445                         push @authorised_values, ""
2446                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2447                         while ( my ( $value, $lib ) =
2448                             $authorised_values_sth->fetchrow_array )
2449                         {
2450                             push @authorised_values, $value;
2451                             $authorised_lib{$value} = $lib;
2452                         }
2453                     }
2454                     $subfield_data{marc_value} = CGI::scrolling_list(
2455                         -name     => 'field_value',
2456                         -values   => \@authorised_values,
2457                         -default  => "$value",
2458                         -labels   => \%authorised_lib,
2459                         -size     => 1,
2460                         -tabindex => '',
2461                         -multiple => 0,
2462                     );
2463                 }
2464                 else {
2465                     $subfield_data{marc_value} =
2466 "<input type=\"text\" name=\"field_value\" value=\"$value\" size=\"50\" maxlength=\"255\" />";
2467                 }
2468                 push( @loop_data, \%subfield_data );
2469             }
2470         }
2471     }
2472     my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
2473       if ( $itemrecord && $itemrecord->field($itemtagfield) );
2474     return {
2475         'itemtagfield'    => $itemtagfield,
2476         'itemtagsubfield' => $itemtagsubfield,
2477         'itemnumber'      => $itemnumber,
2478         'iteminformation' => \@loop_data
2479     };
2480 }
2481 #"
2482
2483 #
2484 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2485 # at the same time
2486 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2487 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2488 # =head2 ModZebrafiles
2489
2490 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2491
2492 # =cut
2493
2494 # sub ModZebrafiles {
2495
2496 #     my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2497
2498 #     my $op;
2499 #     my $zebradir =
2500 #       C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2501 #     unless ( opendir( DIR, "$zebradir" ) ) {
2502 #         warn "$zebradir not found";
2503 #         return;
2504 #     }
2505 #     closedir DIR;
2506 #     my $filename = $zebradir . $biblionumber;
2507
2508 #     if ($record) {
2509 #         open( OUTPUT, ">", $filename . ".xml" );
2510 #         print OUTPUT $record;
2511 #         close OUTPUT;
2512 #     }
2513 # }
2514
2515 =head2 ModZebra
2516
2517 =over 4
2518
2519 ModZebra( $biblionumber, $op, $server, $oldRecord, $newRecord );
2520
2521     $biblionumber is the biblionumber we want to index
2522     $op is specialUpdate or delete, and is used to know what we want to do
2523     $server is the server that we want to update
2524     $oldRecord is the MARC::Record containing the previous version of the record.  This is used only when 
2525       NoZebra=1, as NoZebra indexing needs to know the previous version of a record in order to
2526       do an update.
2527     $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.
2528     
2529 =back
2530
2531 =cut
2532
2533 sub ModZebra {
2534 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2535     my ( $biblionumber, $op, $server, $oldRecord, $newRecord ) = @_;
2536     my $dbh=C4::Context->dbh;
2537
2538     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2539     # at the same time
2540     # replaced by a zebraqueue table, that is filled with ModZebra to run.
2541     # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2542
2543     if (C4::Context->preference("NoZebra")) {
2544         # lock the nozebra table : we will read index lines, update them in Perl process
2545         # and write everything in 1 transaction.
2546         # lock the table to avoid someone else overwriting what we are doing
2547         $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE, auth_subfield_structure READ');
2548         my %result; # the result hash that will be built by deletion / add, and written on mySQL at the end, to improve speed
2549         if ($op eq 'specialUpdate') {
2550             # OK, we have to add or update the record
2551             # 1st delete (virtually, in indexes), if record actually exists
2552             if ($oldRecord) { 
2553                 %result = _DelBiblioNoZebra($biblionumber,$oldRecord,$server);
2554             }
2555             # ... add the record
2556             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
2557         } else {
2558             # it's a deletion, delete the record...
2559             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2560             %result=_DelBiblioNoZebra($biblionumber,$oldRecord,$server);
2561         }
2562         # ok, now update the database...
2563         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2564         foreach my $key (keys %result) {
2565             foreach my $index (keys %{$result{$key}}) {
2566                 $sth->execute($result{$key}->{$index}, $server, $key, $index);
2567             }
2568         }
2569         $dbh->do('UNLOCK TABLES');
2570     } else {
2571         #
2572         # we use zebra, just fill zebraqueue table
2573         #
2574         my $check_sql = "SELECT COUNT(*) FROM zebraqueue 
2575                          WHERE server = ?
2576                          AND   biblio_auth_number = ?
2577                          AND   operation = ?
2578                          AND   done = 0";
2579         my $check_sth = $dbh->prepare_cached($check_sql);
2580         $check_sth->execute($server, $biblionumber, $op);
2581         my ($count) = $check_sth->fetchrow_array;
2582         $check_sth->finish();
2583         if ($count == 0) {
2584             my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2585             $sth->execute($biblionumber,$server,$op);
2586             $sth->finish;
2587         }
2588     }
2589 }
2590
2591 =head2 GetNoZebraIndexes
2592
2593     %indexes = GetNoZebraIndexes;
2594     
2595     return the data from NoZebraIndexes syspref.
2596
2597 =cut
2598
2599 sub GetNoZebraIndexes {
2600     my $no_zebra_indexes = C4::Context->preference('NoZebraIndexes');
2601     my %indexes;
2602     INDEX: foreach my $line (split /['"],[\n\r]*/,$no_zebra_indexes) {
2603         $line =~ /(.*)=>(.*)/;
2604         my $index = $1; # initial ' or " is removed afterwards
2605         my $fields = $2;
2606         $index =~ s/'|"|\s//g;
2607         $fields =~ s/'|"|\s//g;
2608         $indexes{$index}=$fields;
2609     }
2610     return %indexes;
2611 }
2612
2613 =head1 INTERNAL FUNCTIONS
2614
2615 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2616
2617     function to delete a biblio in NoZebra indexes
2618     This function does NOT delete anything in database : it reads all the indexes entries
2619     that have to be deleted & delete them in the hash
2620     The SQL part is done either :
2621     - after the Add if we are modifying a biblio (delete + add again)
2622     - immediatly after this sub if we are doing a true deletion.
2623     $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2624
2625 =cut
2626
2627
2628 sub _DelBiblioNoZebra {
2629     my ($biblionumber, $record, $server)=@_;
2630     
2631     # Get the indexes
2632     my $dbh = C4::Context->dbh;
2633     # Get the indexes
2634     my %index;
2635     my $title;
2636     if ($server eq 'biblioserver') {
2637         %index=GetNoZebraIndexes;
2638         # get title of the record (to store the 10 first letters with the index)
2639         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title', ''); # FIXME: should be GetFrameworkCode($biblionumber) ??
2640         $title = lc($record->subfield($titletag,$titlesubfield));
2641     } else {
2642         # for authorities, the "title" is the $a mainentry
2643         my ($auth_type_tag, $auth_type_sf) = C4::AuthoritiesMarc::get_auth_type_location();
2644         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield($auth_type_tag, $auth_type_sf));
2645         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2646         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2647         $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
2648         $index{'mainentry'}    = $authref->{'auth_tag_to_report'}.'*';
2649         $index{'auth_type'}    = "${auth_type_tag}${auth_type_sf}";
2650     }
2651     
2652     my %result;
2653     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2654     $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2655     # limit to 10 char, should be enough, and limit the DB size
2656     $title = substr($title,0,10);
2657     #parse each field
2658     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2659     foreach my $field ($record->fields()) {
2660         #parse each subfield
2661         next if $field->tag <10;
2662         foreach my $subfield ($field->subfields()) {
2663             my $tag = $field->tag();
2664             my $subfieldcode = $subfield->[0];
2665             my $indexed=0;
2666             # check each index to see if the subfield is stored somewhere
2667             # otherwise, store it in __RAW__ index
2668             foreach my $key (keys %index) {
2669 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2670                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2671                     $indexed=1;
2672                     my $line= lc $subfield->[1];
2673                     # remove meaningless value in the field...
2674                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2675                     # ... and split in words
2676                     foreach (split / /,$line) {
2677                         next unless $_; # skip  empty values (multiple spaces)
2678                         # if the entry is already here, do nothing, the biblionumber has already be removed
2679                         unless ( defined( $result{$key}->{$_} ) && ( $result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) ) {
2680                             # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2681                             $sth2->execute($server,$key,$_);
2682                             my $existing_biblionumbers = $sth2->fetchrow;
2683                             # it exists
2684                             if ($existing_biblionumbers) {
2685 #                                 warn " existing for $key $_: $existing_biblionumbers";
2686                                 $result{$key}->{$_} =$existing_biblionumbers;
2687                                 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2688                             }
2689                         }
2690                     }
2691                 }
2692             }
2693             # the subfield is not indexed, store it in __RAW__ index anyway
2694             unless ($indexed) {
2695                 my $line= lc $subfield->[1];
2696                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2697                 # ... and split in words
2698                 foreach (split / /,$line) {
2699                     next unless $_; # skip  empty values (multiple spaces)
2700                     # if the entry is already here, do nothing, the biblionumber has already be removed
2701                     unless ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
2702                         # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2703                         $sth2->execute($server,'__RAW__',$_);
2704                         my $existing_biblionumbers = $sth2->fetchrow;
2705                         # it exists
2706                         if ($existing_biblionumbers) {
2707                             $result{'__RAW__'}->{$_} =$existing_biblionumbers;
2708                             $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2709                         }
2710                     }
2711                 }
2712             }
2713         }
2714     }
2715     return %result;
2716 }
2717
2718 =head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
2719
2720     function to add a biblio in NoZebra indexes
2721
2722 =cut
2723
2724 sub _AddBiblioNoZebra {
2725     my ($biblionumber, $record, $server, %result)=@_;
2726     my $dbh = C4::Context->dbh;
2727     # Get the indexes
2728     my %index;
2729     my $title;
2730     if ($server eq 'biblioserver') {
2731         %index=GetNoZebraIndexes;
2732         # get title of the record (to store the 10 first letters with the index)
2733         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title', ''); # FIXME: should be GetFrameworkCode($biblionumber) ??
2734         $title = lc($record->subfield($titletag,$titlesubfield));
2735     } else {
2736         # warn "server : $server";
2737         # for authorities, the "title" is the $a mainentry
2738         my ($auth_type_tag, $auth_type_sf) = C4::AuthoritiesMarc::get_auth_type_location();
2739         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield($auth_type_tag, $auth_type_sf));
2740         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
2741         $title = $record->subfield($authref->{auth_tag_to_report},'a');
2742         $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
2743         $index{'mainentry'}     = $authref->{auth_tag_to_report}.'*';
2744         $index{'auth_type'}    = "${auth_type_tag}${auth_type_sf}";
2745     }
2746
2747     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2748     $title =~ s/ |\.|,|;|\[|\]|\(|\)|\*|-|'|:|=|\r|\n//g;
2749     # limit to 10 char, should be enough, and limit the DB size
2750     $title = substr($title,0,10);
2751     #parse each field
2752     my $sth2=$dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2753     foreach my $field ($record->fields()) {
2754         #parse each subfield
2755         ###FIXME: impossible to index a 001-009 value with NoZebra
2756         next if $field->tag <10;
2757         foreach my $subfield ($field->subfields()) {
2758             my $tag = $field->tag();
2759             my $subfieldcode = $subfield->[0];
2760             my $indexed=0;
2761 #             warn "INDEXING :".$subfield->[1];
2762             # check each index to see if the subfield is stored somewhere
2763             # otherwise, store it in __RAW__ index
2764             foreach my $key (keys %index) {
2765 #                 warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2766                 if ($index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/) {
2767                     $indexed=1;
2768                     my $line= lc $subfield->[1];
2769                     # remove meaningless value in the field...
2770                     $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2771                     # ... and split in words
2772                     foreach (split / /,$line) {
2773                         next unless $_; # skip  empty values (multiple spaces)
2774                         # if the entry is already here, improve weight
2775 #                         warn "managing $_";
2776                         if ( exists $result{$key}->{$_} && $result{$key}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d+);/) {
2777                             my $weight = $1 + 1;
2778                             $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2779                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2780                         } else {
2781                             # get the value if it exist in the nozebra table, otherwise, create it
2782                             $sth2->execute($server,$key,$_);
2783                             my $existing_biblionumbers = $sth2->fetchrow;
2784                             # it exists
2785                             if ($existing_biblionumbers) {
2786                                 $result{$key}->{"$_"} =$existing_biblionumbers;
2787                                 my $weight = defined $1 ? $1 + 1 : 1;
2788                                 $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
2789                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
2790                             # create a new ligne for this entry
2791                             } else {
2792 #                             warn "INSERT : $server / $key / $_";
2793                                 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
2794                                 $result{$key}->{"$_"}.="$biblionumber,$title-1;";
2795                             }
2796                         }
2797                     }
2798                 }
2799             }
2800             # the subfield is not indexed, store it in __RAW__ index anyway
2801             unless ($indexed) {
2802                 my $line= lc $subfield->[1];
2803                 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
2804                 # ... and split in words
2805                 foreach (split / /,$line) {
2806                     next unless $_; # skip  empty values (multiple spaces)
2807                     # if the entry is already here, improve weight
2808                     my $tmpstr = $result{'__RAW__'}->{"$_"} || "";
2809                     if ($tmpstr =~ /$biblionumber,\Q$title\E\-(\d+);/) {
2810                         my $weight=$1+1;
2811                         $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2812                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2813                     } else {
2814                         # get the value if it exist in the nozebra table, otherwise, create it
2815                         $sth2->execute($server,'__RAW__',$_);
2816                         my $existing_biblionumbers = $sth2->fetchrow;
2817                         # it exists
2818                         if ($existing_biblionumbers) {
2819                             $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
2820                             my $weight = ($1 ? $1 : 0) + 1;
2821                             $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
2822                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
2823                         # create a new ligne for this entry
2824                         } else {
2825                             $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).',  indexname="__RAW__",value='.$dbh->quote($_));
2826                             $result{'__RAW__'}->{"$_"}.="$biblionumber,$title-1;";
2827                         }
2828                     }
2829                 }
2830             }
2831         }
2832     }
2833     return %result;
2834 }
2835
2836
2837 =head2 _find_value
2838
2839 =over 4
2840
2841 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2842
2843 Find the given $subfield in the given $tag in the given
2844 MARC::Record $record.  If the subfield is found, returns
2845 the (indicators, value) pair; otherwise, (undef, undef) is
2846 returned.
2847
2848 PROPOSITION :
2849 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2850 I suggest we export it from this module.
2851
2852 =back
2853
2854 =cut
2855
2856 sub _find_value {
2857     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2858     my @result;
2859     my $indicator;
2860     if ( $tagfield < 10 ) {
2861         if ( $record->field($tagfield) ) {
2862             push @result, $record->field($tagfield)->data();
2863         }
2864         else {
2865             push @result, "";
2866         }
2867     }
2868     else {
2869         foreach my $field ( $record->field($tagfield) ) {
2870             my @subfields = $field->subfields();
2871             foreach my $subfield (@subfields) {
2872                 if ( @$subfield[0] eq $insubfield ) {
2873                     push @result, @$subfield[1];
2874                     $indicator = $field->indicator(1) . $field->indicator(2);
2875                 }
2876             }
2877         }
2878     }
2879     return ( $indicator, @result );
2880 }
2881
2882 =head2 _koha_marc_update_bib_ids
2883
2884 =over 4
2885
2886 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
2887
2888 Internal function to add or update biblionumber and biblioitemnumber to
2889 the MARC XML.
2890
2891 =back
2892
2893 =cut
2894
2895 sub _koha_marc_update_bib_ids {
2896     my ($record, $frameworkcode, $biblionumber, $biblioitemnumber) = @_;
2897
2898     # we must add bibnum and bibitemnum in MARC::Record...
2899     # we build the new field with biblionumber and biblioitemnumber
2900     # we drop the original field
2901     # we add the new builded field.
2902     my ($biblio_tag, $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
2903     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
2904
2905     if ($biblio_tag != $biblioitem_tag) {
2906         # biblionumber & biblioitemnumber are in different fields
2907
2908         # deal with biblionumber
2909         my ($new_field, $old_field);
2910         if ($biblio_tag < 10) {
2911             $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
2912         } else {
2913             $new_field =
2914               MARC::Field->new( $biblio_tag, '', '',
2915                 "$biblio_subfield" => $biblionumber );
2916         }
2917
2918         # drop old field and create new one...
2919         $old_field = $record->field($biblio_tag);
2920         $record->delete_field($old_field) if $old_field;
2921         $record->append_fields($new_field);
2922
2923         # deal with biblioitemnumber
2924         if ($biblioitem_tag < 10) {
2925             $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
2926         } else {
2927             $new_field =
2928               MARC::Field->new( $biblioitem_tag, '', '',
2929                 "$biblioitem_subfield" => $biblioitemnumber, );
2930         }
2931         # drop old field and create new one...
2932         $old_field = $record->field($biblioitem_tag);
2933         $record->delete_field($old_field) if $old_field;
2934         $record->insert_fields_ordered($new_field);
2935
2936     } else {
2937         # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
2938         my $new_field = MARC::Field->new(
2939             $biblio_tag, '', '',
2940             "$biblio_subfield" => $biblionumber,
2941             "$biblioitem_subfield" => $biblioitemnumber
2942         );
2943
2944         # drop old field and create new one...
2945         my $old_field = $record->field($biblio_tag);
2946         $record->delete_field($old_field) if $old_field;
2947         $record->insert_fields_ordered($new_field);
2948     }
2949 }
2950
2951 =head2 _koha_marc_update_biblioitem_cn_sort
2952
2953 =over 4
2954
2955 _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
2956
2957 =back
2958
2959 Given a MARC bib record and the biblioitem hash, update the
2960 subfield that contains a copy of the value of biblioitems.cn_sort.
2961
2962 =cut
2963
2964 sub _koha_marc_update_biblioitem_cn_sort {
2965     my $marc = shift;
2966     my $biblioitem = shift;
2967     my $frameworkcode= shift;
2968
2969     my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.cn_sort",$frameworkcode);
2970     return unless $biblioitem_tag;
2971
2972     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
2973
2974     if (my $field = $marc->field($biblioitem_tag)) {
2975         $field->delete_subfield(code => $biblioitem_subfield);
2976         if ($cn_sort ne '') {
2977             $field->add_subfields($biblioitem_subfield => $cn_sort);
2978         }
2979     } else {
2980         # if we get here, no biblioitem tag is present in the MARC record, so
2981         # we'll create it if $cn_sort is not empty -- this would be
2982         # an odd combination of events, however
2983         if ($cn_sort) {
2984             $marc->insert_grouped_field(MARC::Field->new($biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort));
2985         }
2986     }
2987 }
2988
2989 =head2 _koha_add_biblio
2990
2991 =over 4
2992
2993 my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
2994
2995 Internal function to add a biblio ($biblio is a hash with the values)
2996
2997 =back
2998
2999 =cut
3000
3001 sub _koha_add_biblio {
3002     my ( $dbh, $biblio, $frameworkcode ) = @_;
3003
3004     my $error;
3005
3006     # set the series flag
3007     my $serial = 0;
3008     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
3009
3010     my $query = 
3011         "INSERT INTO biblio
3012         SET frameworkcode = ?,
3013             author = ?,
3014             title = ?,
3015             unititle =?,
3016             notes = ?,
3017             serial = ?,
3018             seriestitle = ?,
3019             copyrightdate = ?,
3020             datecreated=NOW(),
3021             abstract = ?
3022         ";
3023     my $sth = $dbh->prepare($query);
3024     $sth->execute(
3025         $frameworkcode,
3026         $biblio->{'author'},
3027         $biblio->{'title'},
3028         $biblio->{'unititle'},
3029         $biblio->{'notes'},
3030         $serial,
3031         $biblio->{'seriestitle'},
3032         $biblio->{'copyrightdate'},
3033         $biblio->{'abstract'}
3034     );
3035
3036     my $biblionumber = $dbh->{'mysql_insertid'};
3037     if ( $dbh->errstr ) {
3038         $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
3039         warn $error;
3040     }
3041
3042     $sth->finish();
3043     #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3044     return ($biblionumber,$error);
3045 }
3046
3047 =head2 _koha_modify_biblio
3048
3049 =over 4
3050
3051 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3052
3053 Internal function for updating the biblio table
3054
3055 =back
3056
3057 =cut
3058
3059 sub _koha_modify_biblio {
3060     my ( $dbh, $biblio, $frameworkcode ) = @_;
3061     my $error;
3062
3063     my $query = "
3064         UPDATE biblio
3065         SET    frameworkcode = ?,
3066                author = ?,
3067                title = ?,
3068                unititle = ?,
3069                notes = ?,
3070                serial = ?,
3071                seriestitle = ?,
3072                copyrightdate = ?,
3073                abstract = ?
3074         WHERE  biblionumber = ?
3075         "
3076     ;
3077     my $sth = $dbh->prepare($query);
3078     
3079     $sth->execute(
3080         $frameworkcode,
3081         $biblio->{'author'},
3082         $biblio->{'title'},
3083         $biblio->{'unititle'},
3084         $biblio->{'notes'},
3085         $biblio->{'serial'},
3086         $biblio->{'seriestitle'},
3087         $biblio->{'copyrightdate'},
3088         $biblio->{'abstract'},
3089         $biblio->{'biblionumber'}
3090     ) if $biblio->{'biblionumber'};
3091
3092     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3093         $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
3094         warn $error;
3095     }
3096     return ( $biblio->{'biblionumber'},$error );
3097 }
3098
3099 =head2 _koha_modify_biblioitem_nonmarc
3100
3101 =over 4
3102
3103 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3104
3105 Updates biblioitems row except for marc and marcxml, which should be changed
3106 via ModBiblioMarc
3107
3108 =back
3109
3110 =cut
3111
3112 sub _koha_modify_biblioitem_nonmarc {
3113     my ( $dbh, $biblioitem ) = @_;
3114     my $error;
3115
3116     # re-calculate the cn_sort, it may have changed
3117     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3118
3119     my $query = 
3120     "UPDATE biblioitems 
3121     SET biblionumber    = ?,
3122         volume          = ?,
3123         number          = ?,
3124         itemtype        = ?,
3125         isbn            = ?,
3126         issn            = ?,
3127         publicationyear = ?,
3128         publishercode   = ?,
3129         volumedate      = ?,
3130         volumedesc      = ?,
3131         collectiontitle = ?,
3132         collectionissn  = ?,
3133         collectionvolume= ?,
3134         editionstatement= ?,
3135         editionresponsibility = ?,
3136         illus           = ?,
3137         pages           = ?,
3138         notes           = ?,
3139         size            = ?,
3140         place           = ?,
3141         lccn            = ?,
3142         url             = ?,
3143         cn_source       = ?,
3144         cn_class        = ?,
3145         cn_item         = ?,
3146         cn_suffix       = ?,
3147         cn_sort         = ?,
3148         totalissues     = ?
3149         where biblioitemnumber = ?
3150         ";
3151     my $sth = $dbh->prepare($query);
3152     $sth->execute(
3153         $biblioitem->{'biblionumber'},
3154         $biblioitem->{'volume'},
3155         $biblioitem->{'number'},
3156         $biblioitem->{'itemtype'},
3157         $biblioitem->{'isbn'},
3158         $biblioitem->{'issn'},
3159         $biblioitem->{'publicationyear'},
3160         $biblioitem->{'publishercode'},
3161         $biblioitem->{'volumedate'},
3162         $biblioitem->{'volumedesc'},
3163         $biblioitem->{'collectiontitle'},
3164         $biblioitem->{'collectionissn'},
3165         $biblioitem->{'collectionvolume'},
3166         $biblioitem->{'editionstatement'},
3167         $biblioitem->{'editionresponsibility'},
3168         $biblioitem->{'illus'},
3169         $biblioitem->{'pages'},
3170         $biblioitem->{'bnotes'},
3171         $biblioitem->{'size'},
3172         $biblioitem->{'place'},
3173         $biblioitem->{'lccn'},
3174         $biblioitem->{'url'},
3175         $biblioitem->{'biblioitems.cn_source'},
3176         $biblioitem->{'cn_class'},
3177         $biblioitem->{'cn_item'},
3178         $biblioitem->{'cn_suffix'},
3179         $cn_sort,
3180         $biblioitem->{'totalissues'},
3181         $biblioitem->{'biblioitemnumber'}
3182     );
3183     if ( $dbh->errstr ) {
3184         $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
3185         warn $error;
3186     }
3187     return ($biblioitem->{'biblioitemnumber'},$error);
3188 }
3189
3190 =head2 _koha_add_biblioitem
3191
3192 =over 4
3193
3194 my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3195
3196 Internal function to add a biblioitem
3197
3198 =back
3199
3200 =cut
3201
3202 sub _koha_add_biblioitem {
3203     my ( $dbh, $biblioitem ) = @_;
3204     my $error;
3205
3206     my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3207     my $query =
3208     "INSERT INTO biblioitems SET
3209         biblionumber    = ?,
3210         volume          = ?,
3211         number          = ?,
3212         itemtype        = ?,
3213         isbn            = ?,
3214         issn            = ?,
3215         publicationyear = ?,
3216         publishercode   = ?,
3217         volumedate      = ?,
3218         volumedesc      = ?,
3219         collectiontitle = ?,
3220         collectionissn  = ?,
3221         collectionvolume= ?,
3222         editionstatement= ?,
3223         editionresponsibility = ?,
3224         illus           = ?,
3225         pages           = ?,
3226         notes           = ?,
3227         size            = ?,
3228         place           = ?,
3229         lccn            = ?,
3230         marc            = ?,
3231         url             = ?,
3232         cn_source       = ?,
3233         cn_class        = ?,
3234         cn_item         = ?,
3235         cn_suffix       = ?,
3236         cn_sort         = ?,
3237         totalissues     = ?
3238         ";
3239     my $sth = $dbh->prepare($query);
3240     $sth->execute(
3241         $biblioitem->{'biblionumber'},
3242         $biblioitem->{'volume'},
3243         $biblioitem->{'number'},
3244         $biblioitem->{'itemtype'},
3245         $biblioitem->{'isbn'},
3246         $biblioitem->{'issn'},
3247         $biblioitem->{'publicationyear'},
3248         $biblioitem->{'publishercode'},
3249         $biblioitem->{'volumedate'},
3250         $biblioitem->{'volumedesc'},
3251         $biblioitem->{'collectiontitle'},
3252         $biblioitem->{'collectionissn'},
3253         $biblioitem->{'collectionvolume'},
3254         $biblioitem->{'editionstatement'},
3255         $biblioitem->{'editionresponsibility'},
3256         $biblioitem->{'illus'},
3257         $biblioitem->{'pages'},
3258         $biblioitem->{'bnotes'},
3259         $biblioitem->{'size'},
3260         $biblioitem->{'place'},
3261         $biblioitem->{'lccn'},
3262         $biblioitem->{'marc'},
3263         $biblioitem->{'url'},
3264         $biblioitem->{'biblioitems.cn_source'},
3265         $biblioitem->{'cn_class'},
3266         $biblioitem->{'cn_item'},
3267         $biblioitem->{'cn_suffix'},
3268         $cn_sort,
3269         $biblioitem->{'totalissues'}
3270     );
3271     my $bibitemnum = $dbh->{'mysql_insertid'};
3272     if ( $dbh->errstr ) {
3273         $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
3274         warn $error;
3275     }
3276     $sth->finish();
3277     return ($bibitemnum,$error);
3278 }
3279
3280 =head2 _koha_delete_biblio
3281
3282 =over 4
3283
3284 $error = _koha_delete_biblio($dbh,$biblionumber);
3285
3286 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3287
3288 C<$dbh> - the database handle
3289 C<$biblionumber> - the biblionumber of the biblio to be deleted
3290
3291 =back
3292
3293 =cut
3294
3295 # FIXME: add error handling
3296
3297 sub _koha_delete_biblio {
3298     my ( $dbh, $biblionumber ) = @_;
3299
3300     # get all the data for this biblio
3301     my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3302     $sth->execute($biblionumber);
3303
3304     if ( my $data = $sth->fetchrow_hashref ) {
3305
3306         # save the record in deletedbiblio
3307         # find the fields to save
3308         my $query = "INSERT INTO deletedbiblio SET ";
3309         my @bind  = ();
3310         foreach my $temp ( keys %$data ) {
3311             $query .= "$temp = ?,";
3312             push( @bind, $data->{$temp} );
3313         }
3314
3315         # replace the last , by ",?)"
3316         $query =~ s/\,$//;
3317         my $bkup_sth = $dbh->prepare($query);
3318         $bkup_sth->execute(@bind);
3319         $bkup_sth->finish;
3320
3321         # delete the biblio
3322         my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3323         $del_sth->execute($biblionumber);
3324         $del_sth->finish;
3325     }
3326     $sth->finish;
3327     return undef;
3328 }
3329
3330 =head2 _koha_delete_biblioitems
3331
3332 =over 4
3333
3334 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3335
3336 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3337
3338 C<$dbh> - the database handle
3339 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3340
3341 =back
3342
3343 =cut
3344
3345 # FIXME: add error handling
3346
3347 sub _koha_delete_biblioitems {
3348     my ( $dbh, $biblioitemnumber ) = @_;
3349
3350     # get all the data for this biblioitem
3351     my $sth =
3352       $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3353     $sth->execute($biblioitemnumber);
3354
3355     if ( my $data = $sth->fetchrow_hashref ) {
3356
3357         # save the record in deletedbiblioitems
3358         # find the fields to save
3359         my $query = "INSERT INTO deletedbiblioitems SET ";
3360         my @bind  = ();
3361         foreach my $temp ( keys %$data ) {
3362             $query .= "$temp = ?,";
3363             push( @bind, $data->{$temp} );
3364         }
3365
3366         # replace the last , by ",?)"
3367         $query =~ s/\,$//;
3368         my $bkup_sth = $dbh->prepare($query);
3369         $bkup_sth->execute(@bind);
3370         $bkup_sth->finish;
3371
3372         # delete the biblioitem
3373         my $del_sth =
3374           $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3375         $del_sth->execute($biblioitemnumber);
3376         $del_sth->finish;
3377     }
3378     $sth->finish;
3379     return undef;
3380 }
3381
3382 =head1 UNEXPORTED FUNCTIONS
3383
3384 =head2 ModBiblioMarc
3385
3386     &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3387     
3388     Add MARC data for a biblio to koha 
3389     
3390     Function exported, but should NOT be used, unless you really know what you're doing
3391
3392 =cut
3393
3394 sub ModBiblioMarc {
3395     
3396 # pass the MARC::Record to this function, and it will create the records in the marc field
3397     my ( $record, $biblionumber, $frameworkcode ) = @_;
3398     my $dbh = C4::Context->dbh;
3399     my @fields = $record->fields();
3400     if ( !$frameworkcode ) {
3401         $frameworkcode = "";
3402     }
3403     my $sth =
3404       $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3405     $sth->execute( $frameworkcode, $biblionumber );
3406     $sth->finish;
3407     my $encoding = C4::Context->preference("marcflavour");
3408
3409     # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3410     if ( $encoding eq "UNIMARC" ) {
3411         my $string = $record->subfield( 100, "a" );
3412         if ( ($string) && ( length($record->subfield( 100, "a" )) == 35 ) ) {
3413             my $f100 = $record->field(100);
3414             $record->delete_field($f100);
3415         }
3416         else {
3417             $string = POSIX::strftime( "%Y%m%d", localtime );
3418             $string =~ s/\-//g;
3419             $string = sprintf( "%-*s", 35, $string );
3420         }
3421         substr( $string, 22, 6, "frey50" );
3422         unless ( $record->subfield( 100, "a" ) ) {
3423             $record->insert_grouped_field(
3424                 MARC::Field->new( 100, "", "", "a" => $string ) );
3425         }
3426     }
3427     my $oldRecord;
3428     if (C4::Context->preference("NoZebra")) {
3429         # only NoZebra indexing needs to have
3430         # the previous version of the record
3431         $oldRecord = GetMarcBiblio($biblionumber);
3432     }
3433     $sth =
3434       $dbh->prepare(
3435         "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
3436     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
3437         $biblionumber );
3438     $sth->finish;
3439     ModZebra($biblionumber,"specialUpdate","biblioserver",$oldRecord,$record);
3440     return $biblionumber;
3441 }
3442
3443 =head2 z3950_extended_services
3444
3445 z3950_extended_services($serviceType,$serviceOptions,$record);
3446
3447     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.
3448
3449 C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate
3450
3451 C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:
3452
3453     action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.
3454
3455 and maybe
3456
3457     recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
3458     syntax => the record syntax (transfer syntax)
3459     databaseName = Database from connection object
3460
3461     To set serviceOptions, call set_service_options($serviceType)
3462
3463 C<$record> the record, if one is needed for the service type
3464
3465     A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().
3466
3467 =cut
3468
3469 sub z3950_extended_services {
3470     my ( $server, $serviceType, $action, $serviceOptions ) = @_;
3471
3472     # get our connection object
3473     my $Zconn = C4::Context->Zconn( $server, 0, 1 );
3474
3475     # create a new package object
3476     my $Zpackage = $Zconn->package();
3477
3478     # set our options
3479     $Zpackage->option( action => $action );
3480
3481     if ( $serviceOptions->{'databaseName'} ) {
3482         $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
3483     }
3484     if ( $serviceOptions->{'recordIdNumber'} ) {
3485         $Zpackage->option(
3486             recordIdNumber => $serviceOptions->{'recordIdNumber'} );
3487     }
3488     if ( $serviceOptions->{'recordIdOpaque'} ) {
3489         $Zpackage->option(
3490             recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
3491     }
3492
3493  # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
3494  #if ($serviceType eq 'itemorder') {
3495  #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
3496  #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
3497  #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
3498  #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
3499  #}
3500
3501     if ( $serviceOptions->{record} ) {
3502         $Zpackage->option( record => $serviceOptions->{record} );
3503
3504         # can be xml or marc
3505         if ( $serviceOptions->{'syntax'} ) {
3506             $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
3507         }
3508     }
3509
3510     # send the request, handle any exception encountered
3511     eval { $Zpackage->send($serviceType) };
3512     if ( $@ && $@->isa("ZOOM::Exception") ) {
3513         return "error:  " . $@->code() . " " . $@->message() . "\n";
3514     }
3515
3516     # free up package resources
3517     $Zpackage->destroy();
3518 }
3519
3520 =head2 set_service_options
3521
3522 my $serviceOptions = set_service_options($serviceType);
3523
3524 C<$serviceType> itemorder,create,drop,commit,update,xmlupdate
3525
3526 Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.
3527
3528 =cut
3529
3530 sub set_service_options {
3531     my ($serviceType) = @_;
3532     my $serviceOptions;
3533
3534 # FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
3535 #   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml
3536
3537     if ( $serviceType eq 'commit' ) {
3538
3539         # nothing to do
3540     }
3541     if ( $serviceType eq 'create' ) {
3542
3543         # nothing to do
3544     }
3545     if ( $serviceType eq 'drop' ) {
3546         die "ERROR: 'drop' not currently supported (by Zebra)";
3547     }
3548     return $serviceOptions;
3549 }
3550
3551 =head3 get_biblio_authorised_values
3552
3553   find the types and values for all authorised values assigned to this biblio.
3554
3555   parameters:
3556     biblionumber
3557     MARC::Record of the bib
3558
3559   returns: a hashref mapping the authorised value to the value set for this biblionumber
3560
3561       $authorised_values = {
3562                              'Scent'     => 'flowery',
3563                              'Audience'  => 'Young Adult',
3564                              'itemtypes' => 'SER',
3565                            };
3566
3567   Notes: forlibrarian should probably be passed in, and called something different.
3568
3569
3570 =cut
3571
3572 sub get_biblio_authorised_values {
3573     my $biblionumber = shift;
3574     my $record       = shift;
3575     
3576     my $forlibrarian = 1; # are we in staff or opac?
3577     my $frameworkcode = GetFrameworkCode( $biblionumber );
3578
3579     my $authorised_values;
3580
3581     my $tagslib = GetMarcStructure( $forlibrarian, $frameworkcode )
3582       or return $authorised_values;
3583
3584     # assume that these entries in the authorised_value table are bibliolevel.
3585     # ones that start with 'item%' are item level.
3586     my $query = q(SELECT distinct authorised_value, kohafield
3587                     FROM marc_subfield_structure
3588                     WHERE authorised_value !=''
3589                       AND (kohafield like 'biblio%'
3590                        OR  kohafield like '') );
3591     my $bibliolevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
3592     
3593     foreach my $tag ( keys( %$tagslib ) ) {
3594         foreach my $subfield ( keys( %{$tagslib->{ $tag }} ) ) {
3595             # warn "checking $subfield. type is: " . ref $tagslib->{ $tag }{ $subfield };
3596             if ( 'HASH' eq ref $tagslib->{ $tag }{ $subfield } ) {
3597                 if ( defined $tagslib->{ $tag }{ $subfield }{'authorised_value'} && exists $bibliolevel_authorised_values->{ $tagslib->{ $tag }{ $subfield }{'authorised_value'} } ) {
3598                     if ( defined $record->field( $tag ) ) {
3599                         my $this_subfield_value = $record->field( $tag )->subfield( $subfield );
3600                         if ( defined $this_subfield_value ) {
3601                             $authorised_values->{ $tagslib->{ $tag }{ $subfield }{'authorised_value'} } = $this_subfield_value;
3602                         }
3603                     }
3604                 }
3605             }
3606         }
3607     }
3608     # warn ( Data::Dumper->Dump( [ $authorised_values ], [ 'authorised_values' ] ) );
3609     return $authorised_values;
3610 }
3611
3612
3613 1;
3614
3615 __END__
3616
3617 =head1 AUTHOR
3618
3619 Koha Developement team <info@koha.org>
3620
3621 Paul POULAIN paul.poulain@free.fr
3622
3623 Joshua Ferraro jmf@liblime.com
3624
3625 =cut