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