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