Bug 8083 follow-up DBRev number
[koha.git] / C4 / Biblio.pm
blobf8ba09924da4174df8aaa525ac538744f5f3a420
1 package C4::Biblio;
3 # Copyright 2000-2002 Katipo Communications
4 # Copyright 2010 BibLibre
5 # Copyright 2011 Equinox Software, Inc.
7 # This file is part of Koha.
9 # Koha is free software; you can redistribute it and/or modify it under the
10 # terms of the GNU General Public License as published by the Free Software
11 # Foundation; either version 2 of the License, or (at your option) any later
12 # version.
14 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License along
19 # with Koha; if not, write to the Free Software Foundation, Inc.,
20 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 use strict;
23 use warnings;
24 use Carp;
26 # use utf8;
27 use MARC::Record;
28 use MARC::File::USMARC;
29 use MARC::File::XML;
30 use POSIX qw(strftime);
32 use C4::Koha;
33 use C4::Dates qw/format_date/;
34 use C4::Log; # logaction
35 use C4::ClassSource;
36 use C4::Charset;
37 use C4::Linker;
38 use C4::OAI::Sets;
40 use vars qw($VERSION @ISA @EXPORT);
42 BEGIN {
43 $VERSION = 3.07.00.049;
45 require Exporter;
46 @ISA = qw( Exporter );
48 # to add biblios
49 # EXPORTED FUNCTIONS.
50 push @EXPORT, qw(
51 &AddBiblio
54 # to get something
55 push @EXPORT, qw(
56 &Get
57 &GetBiblio
58 &GetBiblioData
59 &GetBiblioItemData
60 &GetBiblioItemInfosOf
61 &GetBiblioItemByBiblioNumber
62 &GetBiblioFromItemNumber
63 &GetBiblionumberFromItemnumber
65 &GetRecordValue
66 &GetFieldMapping
67 &SetFieldMapping
68 &DeleteFieldMapping
70 &GetISBDView
72 &GetMarcControlnumber
73 &GetMarcNotes
74 &GetMarcISBN
75 &GetMarcISSN
76 &GetMarcSubjects
77 &GetMarcBiblio
78 &GetMarcAuthors
79 &GetMarcSeries
80 &GetMarcHosts
81 GetMarcUrls
82 &GetUsedMarcStructure
83 &GetXmlBiblio
84 &GetCOinSBiblio
85 &GetMarcPrice
86 &MungeMarcPrice
87 &GetMarcQuantity
89 &GetAuthorisedValueDesc
90 &GetMarcStructure
91 &GetMarcFromKohaField
92 &GetFrameworkCode
93 &TransformKohaToMarc
94 &PrepHostMarcField
96 &CountItemsIssued
97 &CountBiblioInOrders
98 &GetSubscriptionsId
99 &GetHolds
102 # To modify something
103 push @EXPORT, qw(
104 &ModBiblio
105 &ModBiblioframework
106 &ModZebra
107 &UpdateTotalIssues
110 # To delete something
111 push @EXPORT, qw(
112 &DelBiblio
115 # To link headings in a bib record
116 # to authority records.
117 push @EXPORT, qw(
118 &BiblioAutoLink
119 &LinkBibHeadingsToAuthorities
122 # Internal functions
123 # those functions are exported but should not be used
124 # they are usefull is few circumstances, so are exported.
125 # but don't use them unless you're a core developer ;-)
126 push @EXPORT, qw(
127 &ModBiblioMarc
130 # Others functions
131 push @EXPORT, qw(
132 &TransformMarcToKoha
133 &TransformHtmlToMarc2
134 &TransformHtmlToMarc
135 &TransformHtmlToXml
136 &GetNoZebraIndexes
137 prepare_host_field
141 eval {
142 if (C4::Context->ismemcached) {
143 require Memoize::Memcached;
144 import Memoize::Memcached qw(memoize_memcached);
146 memoize_memcached( 'GetMarcStructure',
147 memcached => C4::Context->memcached);
151 =head1 NAME
153 C4::Biblio - cataloging management functions
155 =head1 DESCRIPTION
157 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:
159 =over 4
161 =item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data
163 =item 2. as raw MARC in the Zebra index and storage engine
165 =item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
167 =back
169 In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
171 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.
173 =over 4
175 =item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation
177 =item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases
179 =back
181 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:
183 =over 4
185 =item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection
187 =item 2. _koha_* - low-level internal functions for managing the koha tables
189 =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.
191 =item 4. Zebra functions used to update the Zebra index
193 =item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm
195 =back
197 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 :
199 =over 4
201 =item 1. save datas in biblio and biblioitems table, that gives us a biblionumber and a biblioitemnumber
203 =item 2. add the biblionumber and biblioitemnumber into the MARC records
205 =item 3. save the marc record
207 =back
209 When dealing with items, we must :
211 =over 4
213 =item 1. save the item in items table, that gives us an itemnumber
215 =item 2. add the itemnumber to the item MARC field
217 =item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
219 When modifying a biblio or an item, the behaviour is quite similar.
221 =back
223 =head1 EXPORTED FUNCTIONS
225 =head2 AddBiblio
227 ($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);
229 Exported function (core API) for adding a new biblio to koha.
231 The first argument is a C<MARC::Record> object containing the
232 bib to add, while the second argument is the desired MARC
233 framework code.
235 This function also accepts a third, optional argument: a hashref
236 to additional options. The only defined option is C<defer_marc_save>,
237 which if present and mapped to a true value, causes C<AddBiblio>
238 to omit the call to save the MARC in C<bibilioitems.marc>
239 and C<biblioitems.marcxml> This option is provided B<only>
240 for the use of scripts such as C<bulkmarcimport.pl> that may need
241 to do some manipulation of the MARC record for item parsing before
242 saving it and which cannot afford the performance hit of saving
243 the MARC record twice. Consequently, do not use that option
244 unless you can guarantee that C<ModBiblioMarc> will be called.
246 =cut
248 sub AddBiblio {
249 my $record = shift;
250 my $frameworkcode = shift;
251 my $options = @_ ? shift : undef;
252 my $defer_marc_save = 0;
253 if ( defined $options and exists $options->{'defer_marc_save'} and $options->{'defer_marc_save'} ) {
254 $defer_marc_save = 1;
257 my ( $biblionumber, $biblioitemnumber, $error );
258 my $dbh = C4::Context->dbh;
260 # transform the data into koha-table style data
261 SetUTF8Flag($record);
262 my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
263 ( $biblionumber, $error ) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
264 $olddata->{'biblionumber'} = $biblionumber;
265 ( $biblioitemnumber, $error ) = _koha_add_biblioitem( $dbh, $olddata );
267 _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber );
269 # update MARC subfield that stores biblioitems.cn_sort
270 _koha_marc_update_biblioitem_cn_sort( $record, $olddata, $frameworkcode );
272 # now add the record
273 ModBiblioMarc( $record, $biblionumber, $frameworkcode ) unless $defer_marc_save;
275 # update OAI-PMH sets
276 if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
277 C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
280 logaction( "CATALOGUING", "ADD", $biblionumber, "biblio" ) if C4::Context->preference("CataloguingLog");
281 return ( $biblionumber, $biblioitemnumber );
284 =head2 ModBiblio
286 ModBiblio( $record,$biblionumber,$frameworkcode);
288 Replace an existing bib record identified by C<$biblionumber>
289 with one supplied by the MARC::Record object C<$record>. The embedded
290 item, biblioitem, and biblionumber fields from the previous
291 version of the bib record replace any such fields of those tags that
292 are present in C<$record>. Consequently, ModBiblio() is not
293 to be used to try to modify item records.
295 C<$frameworkcode> specifies the MARC framework to use
296 when storing the modified bib record; among other things,
297 this controls how MARC fields get mapped to display columns
298 in the C<biblio> and C<biblioitems> tables, as well as
299 which fields are used to store embedded item, biblioitem,
300 and biblionumber data for indexing.
302 =cut
304 sub ModBiblio {
305 my ( $record, $biblionumber, $frameworkcode ) = @_;
306 croak "No record" unless $record;
308 if ( C4::Context->preference("CataloguingLog") ) {
309 my $newrecord = GetMarcBiblio($biblionumber);
310 logaction( "CATALOGUING", "MODIFY", $biblionumber, "BEFORE=>" . $newrecord->as_formatted );
313 # Cleaning up invalid fields must be done early or SetUTF8Flag is liable to
314 # throw an exception which probably won't be handled.
315 foreach my $field ($record->fields()) {
316 if (! $field->is_control_field()) {
317 if (scalar($field->subfields()) == 0 || (scalar($field->subfields()) == 1 && $field->subfield('9'))) {
318 $record->delete_field($field);
323 SetUTF8Flag($record);
324 my $dbh = C4::Context->dbh;
326 $frameworkcode = "" if !$frameworkcode || $frameworkcode eq "Default"; # XXX
328 _strip_item_fields($record, $frameworkcode);
330 # update biblionumber and biblioitemnumber in MARC
331 # FIXME - this is assuming a 1 to 1 relationship between
332 # biblios and biblioitems
333 my $sth = $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
334 $sth->execute($biblionumber);
335 my ($biblioitemnumber) = $sth->fetchrow;
336 $sth->finish();
337 _koha_marc_update_bib_ids( $record, $frameworkcode, $biblionumber, $biblioitemnumber );
339 # load the koha-table data object
340 my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
342 # update MARC subfield that stores biblioitems.cn_sort
343 _koha_marc_update_biblioitem_cn_sort( $record, $oldbiblio, $frameworkcode );
345 # update the MARC record (that now contains biblio and items) with the new record data
346 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
348 # modify the other koha tables
349 _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
350 _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
352 # update OAI-PMH sets
353 if(C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
354 C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
357 return 1;
360 =head2 _strip_item_fields
362 _strip_item_fields($record, $frameworkcode)
364 Utility routine to remove item tags from a
365 MARC bib.
367 =cut
369 sub _strip_item_fields {
370 my $record = shift;
371 my $frameworkcode = shift;
372 # get the items before and append them to the biblio before updating the record, atm we just have the biblio
373 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
375 # delete any item fields from incoming record to avoid
376 # duplication or incorrect data - use AddItem() or ModItem()
377 # to change items
378 foreach my $field ( $record->field($itemtag) ) {
379 $record->delete_field($field);
383 =head2 ModBiblioframework
385 ModBiblioframework($biblionumber,$frameworkcode);
387 Exported function to modify a biblio framework
389 =cut
391 sub ModBiblioframework {
392 my ( $biblionumber, $frameworkcode ) = @_;
393 my $dbh = C4::Context->dbh;
394 my $sth = $dbh->prepare( "UPDATE biblio SET frameworkcode=? WHERE biblionumber=?" );
395 $sth->execute( $frameworkcode, $biblionumber );
396 return 1;
399 =head2 DelBiblio
401 my $error = &DelBiblio($biblionumber);
403 Exported function (core API) for deleting a biblio in koha.
404 Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)
405 Also backs it up to deleted* tables
406 Checks to make sure there are not issues on any of the items
407 return:
408 C<$error> : undef unless an error occurs
410 =cut
412 sub DelBiblio {
413 my ($biblionumber) = @_;
414 my $dbh = C4::Context->dbh;
415 my $error; # for error handling
417 # First make sure this biblio has no items attached
418 my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
419 $sth->execute($biblionumber);
420 if ( my $itemnumber = $sth->fetchrow ) {
422 # Fix this to use a status the template can understand
423 $error .= "This Biblio has items attached, please delete them first before deleting this biblio ";
426 return $error if $error;
428 # We delete attached subscriptions
429 require C4::Serials;
430 my $subscriptions = C4::Serials::GetFullSubscriptionsFromBiblionumber($biblionumber);
431 foreach my $subscription (@$subscriptions) {
432 C4::Serials::DelSubscription( $subscription->{subscriptionid} );
435 # We delete any existing holds
436 require C4::Reserves;
437 my ($count, $reserves) = C4::Reserves::GetReservesFromBiblionumber($biblionumber);
438 foreach my $res ( @$reserves ) {
439 C4::Reserves::CancelReserve( $res->{'biblionumber'}, $res->{'itemnumber'}, $res->{'borrowernumber'} );
442 # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
443 # for at least 2 reasons :
444 # - we need to read the biblio if NoZebra is set (to remove it from the indexes
445 # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
446 # 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)
447 my $oldRecord;
448 if ( C4::Context->preference("NoZebra") ) {
450 # only NoZebra indexing needs to have
451 # the previous version of the record
452 $oldRecord = GetMarcBiblio($biblionumber);
454 ModZebra( $biblionumber, "recordDelete", "biblioserver", $oldRecord, undef );
456 # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
457 $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
458 $sth->execute($biblionumber);
459 while ( my $biblioitemnumber = $sth->fetchrow ) {
461 # delete this biblioitem
462 $error = _koha_delete_biblioitems( $dbh, $biblioitemnumber );
463 return $error if $error;
466 # delete biblio from Koha tables and save in deletedbiblio
467 # must do this *after* _koha_delete_biblioitems, otherwise
468 # delete cascade will prevent deletedbiblioitems rows
469 # from being generated by _koha_delete_biblioitems
470 $error = _koha_delete_biblio( $dbh, $biblionumber );
472 logaction( "CATALOGUING", "DELETE", $biblionumber, "" ) if C4::Context->preference("CataloguingLog");
474 return;
478 =head2 BiblioAutoLink
480 my $headings_linked = BiblioAutoLink($record, $frameworkcode)
482 Automatically links headings in a bib record to authorities.
484 =cut
486 sub BiblioAutoLink {
487 my $record = shift;
488 my $frameworkcode = shift;
489 my ( $num_headings_changed, %results );
491 my $linker_module =
492 "C4::Linker::" . ( C4::Context->preference("LinkerModule") || 'Default' );
493 eval { eval "require $linker_module"; };
494 if ($@) {
495 $linker_module = 'C4::Linker::Default';
496 eval "require $linker_module";
498 if ($@) {
499 return 0, 0;
502 my $linker = $linker_module->new(
503 { 'options' => C4::Context->preference("LinkerOptions") } );
504 my ( $headings_changed, undef ) =
505 LinkBibHeadingsToAuthorities( $linker, $record, $frameworkcode, C4::Context->preference("CatalogModuleRelink") || '' );
506 # By default we probably don't want to relink things when cataloging
507 return $headings_changed;
510 =head2 LinkBibHeadingsToAuthorities
512 my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode, [$allowrelink]);
514 Links bib headings to authority records by checking
515 each authority-controlled field in the C<MARC::Record>
516 object C<$marc>, looking for a matching authority record,
517 and setting the linking subfield $9 to the ID of that
518 authority record.
520 If $allowrelink is false, existing authids will never be
521 replaced, regardless of the values of LinkerKeepStale and
522 LinkerRelink.
524 Returns the number of heading links changed in the
525 MARC record.
527 =cut
529 sub LinkBibHeadingsToAuthorities {
530 my $linker = shift;
531 my $bib = shift;
532 my $frameworkcode = shift;
533 my $allowrelink = shift;
534 my %results;
535 require C4::Heading;
536 require C4::AuthoritiesMarc;
538 $allowrelink = 1 unless defined $allowrelink;
539 my $num_headings_changed = 0;
540 foreach my $field ( $bib->fields() ) {
541 my $heading = C4::Heading->new_from_bib_field( $field, $frameworkcode );
542 next unless defined $heading;
544 # check existing $9
545 my $current_link = $field->subfield('9');
547 if ( defined $current_link && (!$allowrelink || !C4::Context->preference('LinkerRelink')) )
549 $results{'linked'}->{ $heading->display_form() }++;
550 next;
553 my ( $authid, $fuzzy ) = $linker->get_link($heading);
554 if ($authid) {
555 $results{ $fuzzy ? 'fuzzy' : 'linked' }
556 ->{ $heading->display_form() }++;
557 next if defined $current_link and $current_link == $authid;
559 $field->delete_subfield( code => '9' ) if defined $current_link;
560 $field->add_subfields( '9', $authid );
561 $num_headings_changed++;
563 else {
564 if ( defined $current_link
565 && (!$allowrelink || C4::Context->preference('LinkerKeepStale')) )
567 $results{'fuzzy'}->{ $heading->display_form() }++;
569 elsif ( C4::Context->preference('AutoCreateAuthorities') ) {
570 my $authtypedata =
571 C4::AuthoritiesMarc::GetAuthType( $heading->auth_type() );
572 my $marcrecordauth = MARC::Record->new();
573 if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
574 $marcrecordauth->leader(' nz a22 o 4500');
575 SetMarcUnicodeFlag( $marcrecordauth, 'MARC21' );
577 my $authfield =
578 MARC::Field->new( $authtypedata->{auth_tag_to_report},
579 '', '', "a" => "" . $field->subfield('a') );
580 map {
581 $authfield->add_subfields( $_->[0] => $_->[1] )
582 if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a" )
583 } $field->subfields();
584 $marcrecordauth->insert_fields_ordered($authfield);
586 # bug 2317: ensure new authority knows it's using UTF-8; currently
587 # only need to do this for MARC21, as MARC::Record->as_xml_record() handles
588 # automatically for UNIMARC (by not transcoding)
589 # FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record
590 # use UTF-8, but as of 2008-08-05, did not want to introduce that kind
591 # of change to a core API just before the 3.0 release.
593 if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
594 $marcrecordauth->insert_fields_ordered(
595 MARC::Field->new(
596 '667', '', '',
597 'a' => "Machine generated authority record."
600 my $cite =
601 $bib->author() . ", "
602 . $bib->title_proper() . ", "
603 . $bib->publication_date() . " ";
604 $cite =~ s/^[\s\,]*//;
605 $cite =~ s/[\s\,]*$//;
606 $cite =
607 "Work cat.: ("
608 . C4::Context->preference('MARCOrgCode') . ")"
609 . $bib->subfield( '999', 'c' ) . ": "
610 . $cite;
611 $marcrecordauth->insert_fields_ordered(
612 MARC::Field->new( '670', '', '', 'a' => $cite ) );
615 # warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted;
617 $authid =
618 C4::AuthoritiesMarc::AddAuthority( $marcrecordauth, '',
619 $heading->auth_type() );
620 $field->add_subfields( '9', $authid );
621 $num_headings_changed++;
622 $results{'added'}->{ $heading->display_form() }++;
624 elsif ( defined $current_link ) {
625 $field->delete_subfield( code => '9' );
626 $num_headings_changed++;
627 $results{'unlinked'}->{ $heading->display_form() }++;
629 else {
630 $results{'unlinked'}->{ $heading->display_form() }++;
635 return $num_headings_changed, \%results;
638 =head2 GetRecordValue
640 my $values = GetRecordValue($field, $record, $frameworkcode);
642 Get MARC fields from a keyword defined in fieldmapping table.
644 =cut
646 sub GetRecordValue {
647 my ( $field, $record, $frameworkcode ) = @_;
648 my $dbh = C4::Context->dbh;
650 my $sth = $dbh->prepare('SELECT fieldcode, subfieldcode FROM fieldmapping WHERE frameworkcode = ? AND field = ?');
651 $sth->execute( $frameworkcode, $field );
653 my @result = ();
655 while ( my $row = $sth->fetchrow_hashref ) {
656 foreach my $field ( $record->field( $row->{fieldcode} ) ) {
657 if ( ( $row->{subfieldcode} ne "" && $field->subfield( $row->{subfieldcode} ) ) ) {
658 foreach my $subfield ( $field->subfield( $row->{subfieldcode} ) ) {
659 push @result, { 'subfield' => $subfield };
662 } elsif ( $row->{subfieldcode} eq "" ) {
663 push @result, { 'subfield' => $field->as_string() };
668 return \@result;
671 =head2 SetFieldMapping
673 SetFieldMapping($framework, $field, $fieldcode, $subfieldcode);
675 Set a Field to MARC mapping value, if it already exists we don't add a new one.
677 =cut
679 sub SetFieldMapping {
680 my ( $framework, $field, $fieldcode, $subfieldcode ) = @_;
681 my $dbh = C4::Context->dbh;
683 my $sth = $dbh->prepare('SELECT * FROM fieldmapping WHERE fieldcode = ? AND subfieldcode = ? AND frameworkcode = ? AND field = ?');
684 $sth->execute( $fieldcode, $subfieldcode, $framework, $field );
685 if ( not $sth->fetchrow_hashref ) {
686 my @args;
687 $sth = $dbh->prepare('INSERT INTO fieldmapping (fieldcode, subfieldcode, frameworkcode, field) VALUES(?,?,?,?)');
689 $sth->execute( $fieldcode, $subfieldcode, $framework, $field );
693 =head2 DeleteFieldMapping
695 DeleteFieldMapping($id);
697 Delete a field mapping from an $id.
699 =cut
701 sub DeleteFieldMapping {
702 my ($id) = @_;
703 my $dbh = C4::Context->dbh;
705 my $sth = $dbh->prepare('DELETE FROM fieldmapping WHERE id = ?');
706 $sth->execute($id);
709 =head2 GetFieldMapping
711 GetFieldMapping($frameworkcode);
713 Get all field mappings for a specified frameworkcode
715 =cut
717 sub GetFieldMapping {
718 my ($framework) = @_;
719 my $dbh = C4::Context->dbh;
721 my $sth = $dbh->prepare('SELECT * FROM fieldmapping where frameworkcode = ?');
722 $sth->execute($framework);
724 my @return;
725 while ( my $row = $sth->fetchrow_hashref ) {
726 push @return, $row;
728 return \@return;
731 =head2 GetBiblioData
733 $data = &GetBiblioData($biblionumber);
735 Returns information about the book with the given biblionumber.
736 C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
737 the C<biblio> and C<biblioitems> tables in the
738 Koha database.
740 In addition, C<$data-E<gt>{subject}> is the list of the book's
741 subjects, separated by C<" , "> (space, comma, space).
742 If there are multiple biblioitems with the given biblionumber, only
743 the first one is considered.
745 =cut
747 sub GetBiblioData {
748 my ($bibnum) = @_;
749 my $dbh = C4::Context->dbh;
751 # my $query = C4::Context->preference('item-level_itypes') ?
752 # " SELECT * , biblioitems.notes AS bnotes, biblio.notes
753 # FROM biblio
754 # LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
755 # WHERE biblio.biblionumber = ?
756 # AND biblioitems.biblionumber = biblio.biblionumber
759 my $query = " SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
760 FROM biblio
761 LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
762 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
763 WHERE biblio.biblionumber = ?
764 AND biblioitems.biblionumber = biblio.biblionumber ";
766 my $sth = $dbh->prepare($query);
767 $sth->execute($bibnum);
768 my $data;
769 $data = $sth->fetchrow_hashref;
770 $sth->finish;
772 return ($data);
773 } # sub GetBiblioData
775 =head2 &GetBiblioItemData
777 $itemdata = &GetBiblioItemData($biblioitemnumber);
779 Looks up the biblioitem with the given biblioitemnumber. Returns a
780 reference-to-hash. The keys are the fields from the C<biblio>,
781 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
782 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
784 =cut
787 sub GetBiblioItemData {
788 my ($biblioitemnumber) = @_;
789 my $dbh = C4::Context->dbh;
790 my $query = "SELECT *,biblioitems.notes AS bnotes
791 FROM biblio LEFT JOIN biblioitems on biblio.biblionumber=biblioitems.biblionumber ";
792 unless ( C4::Context->preference('item-level_itypes') ) {
793 $query .= "LEFT JOIN itemtypes on biblioitems.itemtype=itemtypes.itemtype ";
795 $query .= " WHERE biblioitemnumber = ? ";
796 my $sth = $dbh->prepare($query);
797 my $data;
798 $sth->execute($biblioitemnumber);
799 $data = $sth->fetchrow_hashref;
800 $sth->finish;
801 return ($data);
802 } # sub &GetBiblioItemData
804 =head2 GetBiblioItemByBiblioNumber
806 NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.
808 =cut
810 sub GetBiblioItemByBiblioNumber {
811 my ($biblionumber) = @_;
812 my $dbh = C4::Context->dbh;
813 my $sth = $dbh->prepare("Select * FROM biblioitems WHERE biblionumber = ?");
814 my $count = 0;
815 my @results;
817 $sth->execute($biblionumber);
819 while ( my $data = $sth->fetchrow_hashref ) {
820 push @results, $data;
823 $sth->finish;
824 return @results;
827 =head2 GetBiblionumberFromItemnumber
830 =cut
832 sub GetBiblionumberFromItemnumber {
833 my ($itemnumber) = @_;
834 my $dbh = C4::Context->dbh;
835 my $sth = $dbh->prepare("Select biblionumber FROM items WHERE itemnumber = ?");
837 $sth->execute($itemnumber);
838 my ($result) = $sth->fetchrow;
839 return ($result);
842 =head2 GetBiblioFromItemNumber
844 $item = &GetBiblioFromItemNumber($itemnumber,$barcode);
846 Looks up the item with the given itemnumber. if undef, try the barcode.
848 C<&itemnodata> returns a reference-to-hash whose keys are the fields
849 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
850 database.
852 =cut
855 sub GetBiblioFromItemNumber {
856 my ( $itemnumber, $barcode ) = @_;
857 my $dbh = C4::Context->dbh;
858 my $sth;
859 if ($itemnumber) {
860 $sth = $dbh->prepare(
861 "SELECT * FROM items
862 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
863 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
864 WHERE items.itemnumber = ?"
866 $sth->execute($itemnumber);
867 } else {
868 $sth = $dbh->prepare(
869 "SELECT * FROM items
870 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
871 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
872 WHERE items.barcode = ?"
874 $sth->execute($barcode);
876 my $data = $sth->fetchrow_hashref;
877 $sth->finish;
878 return ($data);
881 =head2 GetISBDView
883 $isbd = &GetISBDView($biblionumber);
885 Return the ISBD view which can be included in opac and intranet
887 =cut
889 sub GetISBDView {
890 my ( $biblionumber, $template ) = @_;
891 my $record = GetMarcBiblio($biblionumber, 1);
892 return undef unless defined $record;
893 my $itemtype = &GetFrameworkCode($biblionumber);
894 my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch", $itemtype );
895 my $tagslib = &GetMarcStructure( 1, $itemtype );
897 my $ISBD = C4::Context->preference('isbd');
898 my $bloc = $ISBD;
899 my $res;
900 my $blocres;
902 foreach my $isbdfield ( split( /#/, $bloc ) ) {
904 # $isbdfield= /(.?.?.?)/;
905 $isbdfield =~ /(\d\d\d)([^\|])?\|(.*)\|(.*)\|(.*)/;
906 my $fieldvalue = $1 || 0;
907 my $subfvalue = $2 || "";
908 my $textbefore = $3;
909 my $analysestring = $4;
910 my $textafter = $5;
912 # warn "==> $1 / $2 / $3 / $4";
913 # my $fieldvalue=substr($isbdfield,0,3);
914 if ( $fieldvalue > 0 ) {
915 my $hasputtextbefore = 0;
916 my @fieldslist = $record->field($fieldvalue);
917 @fieldslist = sort { $a->subfield($holdingbrtagsubf) cmp $b->subfield($holdingbrtagsubf) } @fieldslist if ( $fieldvalue eq $holdingbrtagf );
919 # warn "ERROR IN ISBD DEFINITION at : $isbdfield" unless $fieldvalue;
920 # warn "FV : $fieldvalue";
921 if ( $subfvalue ne "" ) {
922 foreach my $field (@fieldslist) {
923 foreach my $subfield ( $field->subfield($subfvalue) ) {
924 my $calculated = $analysestring;
925 my $tag = $field->tag();
926 if ( $tag < 10 ) {
927 } else {
928 my $subfieldvalue = GetAuthorisedValueDesc( $tag, $subfvalue, $subfield, '', $tagslib );
929 my $tagsubf = $tag . $subfvalue;
930 $calculated =~ s/\{(.?.?.?.?)$tagsubf(.*?)\}/$1$subfieldvalue$2\{$1$tagsubf$2\}/g;
931 if ( $template eq "opac" ) { $calculated =~ s#/cgi-bin/koha/[^/]+/([^.]*.pl\?.*)$#opac-$1#g; }
933 # field builded, store the result
934 if ( $calculated && !$hasputtextbefore ) { # put textbefore if not done
935 $blocres .= $textbefore;
936 $hasputtextbefore = 1;
939 # remove punctuation at start
940 $calculated =~ s/^( |;|:|\.|-)*//g;
941 $blocres .= $calculated;
946 $blocres .= $textafter if $hasputtextbefore;
947 } else {
948 foreach my $field (@fieldslist) {
949 my $calculated = $analysestring;
950 my $tag = $field->tag();
951 if ( $tag < 10 ) {
952 } else {
953 my @subf = $field->subfields;
954 for my $i ( 0 .. $#subf ) {
955 my $valuecode = $subf[$i][1];
956 my $subfieldcode = $subf[$i][0];
957 my $subfieldvalue = GetAuthorisedValueDesc( $tag, $subf[$i][0], $subf[$i][1], '', $tagslib );
958 my $tagsubf = $tag . $subfieldcode;
960 $calculated =~ s/ # replace all {{}} codes by the value code.
961 \{\{$tagsubf\}\} # catch the {{actualcode}}
963 $valuecode # replace by the value code
964 /gx;
966 $calculated =~ s/\{(.?.?.?.?)$tagsubf(.*?)\}/$1$subfieldvalue$2\{$1$tagsubf$2\}/g;
967 if ( $template eq "opac" ) { $calculated =~ s#/cgi-bin/koha/[^/]+/([^.]*.pl\?.*)$#opac-$1#g; }
970 # field builded, store the result
971 if ( $calculated && !$hasputtextbefore ) { # put textbefore if not done
972 $blocres .= $textbefore;
973 $hasputtextbefore = 1;
976 # remove punctuation at start
977 $calculated =~ s/^( |;|:|\.|-)*//g;
978 $blocres .= $calculated;
981 $blocres .= $textafter if $hasputtextbefore;
983 } else {
984 $blocres .= $isbdfield;
987 $res .= $blocres;
989 $res =~ s/\{(.*?)\}//g;
990 $res =~ s/\\n/\n/g;
991 $res =~ s/\n/<br\/>/g;
993 # remove empty ()
994 $res =~ s/\(\)//g;
996 return $res;
999 =head2 GetBiblio
1001 ( $count, @results ) = &GetBiblio($biblionumber);
1003 =cut
1005 sub GetBiblio {
1006 my ($biblionumber) = @_;
1007 my $dbh = C4::Context->dbh;
1008 my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber = ?");
1009 my $count = 0;
1010 my @results;
1011 $sth->execute($biblionumber);
1012 while ( my $data = $sth->fetchrow_hashref ) {
1013 $results[$count] = $data;
1014 $count++;
1015 } # while
1016 $sth->finish;
1017 return ( $count, @results );
1018 } # sub GetBiblio
1020 =head2 GetBiblioItemInfosOf
1022 GetBiblioItemInfosOf(@biblioitemnumbers);
1024 =cut
1026 sub GetBiblioItemInfosOf {
1027 my @biblioitemnumbers = @_;
1029 my $query = '
1030 SELECT biblioitemnumber,
1031 publicationyear,
1032 itemtype
1033 FROM biblioitems
1034 WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
1036 return get_infos_of( $query, 'biblioitemnumber' );
1039 =head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
1041 =head2 GetMarcStructure
1043 $res = GetMarcStructure($forlibrarian,$frameworkcode);
1045 Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
1046 $forlibrarian :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
1047 $frameworkcode : the framework code to read
1049 =cut
1051 # cache for results of GetMarcStructure -- needed
1052 # for batch jobs
1053 our $marc_structure_cache;
1055 sub GetMarcStructure {
1056 my ( $forlibrarian, $frameworkcode ) = @_;
1057 my $dbh = C4::Context->dbh;
1058 $frameworkcode = "" unless $frameworkcode;
1060 if ( defined $marc_structure_cache and exists $marc_structure_cache->{$forlibrarian}->{$frameworkcode} ) {
1061 return $marc_structure_cache->{$forlibrarian}->{$frameworkcode};
1064 # my $sth = $dbh->prepare(
1065 # "SELECT COUNT(*) FROM marc_tag_structure WHERE frameworkcode=?");
1066 # $sth->execute($frameworkcode);
1067 # my ($total) = $sth->fetchrow;
1068 # $frameworkcode = "" unless ( $total > 0 );
1069 my $sth = $dbh->prepare(
1070 "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable
1071 FROM marc_tag_structure
1072 WHERE frameworkcode=?
1073 ORDER BY tagfield"
1075 $sth->execute($frameworkcode);
1076 my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
1078 while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) = $sth->fetchrow ) {
1079 $res->{$tag}->{lib} = ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1080 $res->{$tag}->{tab} = "";
1081 $res->{$tag}->{mandatory} = $mandatory;
1082 $res->{$tag}->{repeatable} = $repeatable;
1085 $sth = $dbh->prepare(
1086 "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue,maxlength
1087 FROM marc_subfield_structure
1088 WHERE frameworkcode=?
1089 ORDER BY tagfield,tagsubfield
1093 $sth->execute($frameworkcode);
1095 my $subfield;
1096 my $authorised_value;
1097 my $authtypecode;
1098 my $value_builder;
1099 my $kohafield;
1100 my $seealso;
1101 my $hidden;
1102 my $isurl;
1103 my $link;
1104 my $defaultvalue;
1105 my $maxlength;
1107 while (
1108 ( $tag, $subfield, $liblibrarian, $libopac, $tab, $mandatory, $repeatable, $authorised_value,
1109 $authtypecode, $value_builder, $kohafield, $seealso, $hidden, $isurl, $link, $defaultvalue,
1110 $maxlength
1112 = $sth->fetchrow
1114 $res->{$tag}->{$subfield}->{lib} = ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
1115 $res->{$tag}->{$subfield}->{tab} = $tab;
1116 $res->{$tag}->{$subfield}->{mandatory} = $mandatory;
1117 $res->{$tag}->{$subfield}->{repeatable} = $repeatable;
1118 $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
1119 $res->{$tag}->{$subfield}->{authtypecode} = $authtypecode;
1120 $res->{$tag}->{$subfield}->{value_builder} = $value_builder;
1121 $res->{$tag}->{$subfield}->{kohafield} = $kohafield;
1122 $res->{$tag}->{$subfield}->{seealso} = $seealso;
1123 $res->{$tag}->{$subfield}->{hidden} = $hidden;
1124 $res->{$tag}->{$subfield}->{isurl} = $isurl;
1125 $res->{$tag}->{$subfield}->{'link'} = $link;
1126 $res->{$tag}->{$subfield}->{defaultvalue} = $defaultvalue;
1127 $res->{$tag}->{$subfield}->{maxlength} = $maxlength;
1130 $marc_structure_cache->{$forlibrarian}->{$frameworkcode} = $res;
1132 return $res;
1135 =head2 GetUsedMarcStructure
1137 The same function as GetMarcStructure except it just takes field
1138 in tab 0-9. (used field)
1140 my $results = GetUsedMarcStructure($frameworkcode);
1142 C<$results> is a ref to an array which each case containts a ref
1143 to a hash which each keys is the columns from marc_subfield_structure
1145 C<$frameworkcode> is the framework code.
1147 =cut
1149 sub GetUsedMarcStructure($) {
1150 my $frameworkcode = shift || '';
1151 my $query = qq/
1152 SELECT *
1153 FROM marc_subfield_structure
1154 WHERE tab > -1
1155 AND frameworkcode = ?
1156 ORDER BY tagfield, tagsubfield
1158 my $sth = C4::Context->dbh->prepare($query);
1159 $sth->execute($frameworkcode);
1160 return $sth->fetchall_arrayref( {} );
1163 =head2 GetMarcFromKohaField
1165 ($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
1167 Returns the MARC fields & subfields mapped to the koha field
1168 for the given frameworkcode
1170 =cut
1172 sub GetMarcFromKohaField {
1173 my ( $kohafield, $frameworkcode ) = @_;
1174 return (0, undef) unless $kohafield and defined $frameworkcode;
1175 my $relations = C4::Context->marcfromkohafield;
1176 if ( my $mf = $relations->{$frameworkcode}->{$kohafield} ) {
1177 return @$mf;
1179 return (0, undef);
1182 =head2 GetMarcBiblio
1184 my $record = GetMarcBiblio($biblionumber, [$embeditems]);
1186 Returns MARC::Record representing bib identified by
1187 C<$biblionumber>. If no bib exists, returns undef.
1188 C<$embeditems>. If set to true, items data are included.
1189 The MARC record contains biblio data, and items data if $embeditems is set to true.
1191 =cut
1193 sub GetMarcBiblio {
1194 my $biblionumber = shift;
1195 my $embeditems = shift || 0;
1196 my $dbh = C4::Context->dbh;
1197 my $sth = $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1198 $sth->execute($biblionumber);
1199 my $row = $sth->fetchrow_hashref;
1200 my $marcxml = StripNonXmlChars( $row->{'marcxml'} );
1201 MARC::File::XML->default_record_format( C4::Context->preference('marcflavour') );
1202 my $record = MARC::Record->new();
1204 if ($marcxml) {
1205 $record = eval { MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour') ) };
1206 if ($@) { warn " problem with :$biblionumber : $@ \n$marcxml"; }
1207 return unless $record;
1209 C4::Biblio::_koha_marc_update_bib_ids($record, '', $biblionumber, $biblionumber);
1210 C4::Biblio::EmbedItemsInMarcBiblio($record, $biblionumber) if ($embeditems);
1212 return $record;
1213 } else {
1214 return undef;
1218 =head2 GetXmlBiblio
1220 my $marcxml = GetXmlBiblio($biblionumber);
1222 Returns biblioitems.marcxml of the biblionumber passed in parameter.
1223 The XML contains both biblio & item datas
1225 =cut
1227 sub GetXmlBiblio {
1228 my ($biblionumber) = @_;
1229 my $dbh = C4::Context->dbh;
1230 my $sth = $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
1231 $sth->execute($biblionumber);
1232 my ($marcxml) = $sth->fetchrow;
1233 return $marcxml;
1236 =head2 GetCOinSBiblio
1238 my $coins = GetCOinSBiblio($record);
1240 Returns the COinS (a span) which can be included in a biblio record
1242 =cut
1244 sub GetCOinSBiblio {
1245 my $record = shift;
1247 # get the coin format
1248 if ( ! $record ) {
1249 return;
1251 my $pos7 = substr $record->leader(), 7, 1;
1252 my $pos6 = substr $record->leader(), 6, 1;
1253 my $mtx;
1254 my $genre;
1255 my ( $aulast, $aufirst ) = ( '', '' );
1256 my $oauthors = '';
1257 my $title = '';
1258 my $subtitle = '';
1259 my $pubyear = '';
1260 my $isbn = '';
1261 my $issn = '';
1262 my $publisher = '';
1263 my $pages = '';
1264 my $titletype = 'b';
1266 # For the purposes of generating COinS metadata, LDR/06-07 can be
1267 # considered the same for UNIMARC and MARC21
1268 my $fmts6;
1269 my $fmts7;
1270 %$fmts6 = (
1271 'a' => 'book',
1272 'b' => 'manuscript',
1273 'c' => 'book',
1274 'd' => 'manuscript',
1275 'e' => 'map',
1276 'f' => 'map',
1277 'g' => 'film',
1278 'i' => 'audioRecording',
1279 'j' => 'audioRecording',
1280 'k' => 'artwork',
1281 'l' => 'document',
1282 'm' => 'computerProgram',
1283 'o' => 'document',
1284 'r' => 'document',
1286 %$fmts7 = (
1287 'a' => 'journalArticle',
1288 's' => 'journal',
1291 $genre = $fmts6->{$pos6} ? $fmts6->{$pos6} : 'book';
1293 if ( $genre eq 'book' ) {
1294 $genre = $fmts7->{$pos7} if $fmts7->{$pos7};
1297 ##### We must transform mtx to a valable mtx and document type ####
1298 if ( $genre eq 'book' ) {
1299 $mtx = 'book';
1300 } elsif ( $genre eq 'journal' ) {
1301 $mtx = 'journal';
1302 $titletype = 'j';
1303 } elsif ( $genre eq 'journalArticle' ) {
1304 $mtx = 'journal';
1305 $genre = 'article';
1306 $titletype = 'a';
1307 } else {
1308 $mtx = 'dc';
1311 $genre = ( $mtx eq 'dc' ) ? "&amp;rft.type=$genre" : "&amp;rft.genre=$genre";
1313 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
1315 # Setting datas
1316 $aulast = $record->subfield( '700', 'a' ) || '';
1317 $aufirst = $record->subfield( '700', 'b' ) || '';
1318 $oauthors = "&amp;rft.au=$aufirst $aulast";
1320 # others authors
1321 if ( $record->field('200') ) {
1322 for my $au ( $record->field('200')->subfield('g') ) {
1323 $oauthors .= "&amp;rft.au=$au";
1326 $title =
1327 ( $mtx eq 'dc' )
1328 ? "&amp;rft.title=" . $record->subfield( '200', 'a' )
1329 : "&amp;rft.title=" . $record->subfield( '200', 'a' ) . "&amp;rft.btitle=" . $record->subfield( '200', 'a' );
1330 $pubyear = $record->subfield( '210', 'd' ) || '';
1331 $publisher = $record->subfield( '210', 'c' ) || '';
1332 $isbn = $record->subfield( '010', 'a' ) || '';
1333 $issn = $record->subfield( '011', 'a' ) || '';
1334 } else {
1336 # MARC21 need some improve
1338 # Setting datas
1339 if ( $record->field('100') ) {
1340 $oauthors .= "&amp;rft.au=" . $record->subfield( '100', 'a' );
1343 # others authors
1344 if ( $record->field('700') ) {
1345 for my $au ( $record->field('700')->subfield('a') ) {
1346 $oauthors .= "&amp;rft.au=$au";
1349 $title = "&amp;rft." . $titletype . "title=" . $record->subfield( '245', 'a' );
1350 $subtitle = $record->subfield( '245', 'b' ) || '';
1351 $title .= $subtitle;
1352 if ($titletype eq 'a') {
1353 $pubyear = $record->field('008') || '';
1354 $pubyear = substr($pubyear->data(), 7, 4) if $pubyear;
1355 $isbn = $record->subfield( '773', 'z' ) || '';
1356 $issn = $record->subfield( '773', 'x' ) || '';
1357 if ($mtx eq 'journal') {
1358 $title .= "&amp;rft.title=" . (($record->subfield( '773', 't' ) || $record->subfield( '773', 'a')));
1359 } else {
1360 $title .= "&amp;rft.btitle=" . (($record->subfield( '773', 't' ) || $record->subfield( '773', 'a')) || '');
1362 foreach my $rel ($record->subfield( '773', 'g' )) {
1363 if ($pages) {
1364 $pages .= ', ';
1366 $pages .= $rel;
1368 } else {
1369 $pubyear = $record->subfield( '260', 'c' ) || '';
1370 $publisher = $record->subfield( '260', 'b' ) || '';
1371 $isbn = $record->subfield( '020', 'a' ) || '';
1372 $issn = $record->subfield( '022', 'a' ) || '';
1376 my $coins_value =
1377 "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&amp;rft.pages=$pages";
1378 $coins_value =~ s/(\ |&[^a])/\+/g;
1379 $coins_value =~ s/\"/\&quot\;/g;
1381 #<!-- 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="
1383 return $coins_value;
1387 =head2 GetMarcPrice
1389 return the prices in accordance with the Marc format.
1390 =cut
1392 sub GetMarcPrice {
1393 my ( $record, $marcflavour ) = @_;
1394 my @listtags;
1395 my $subfield;
1397 if ( $marcflavour eq "MARC21" ) {
1398 @listtags = ('345', '020');
1399 $subfield="c";
1400 } elsif ( $marcflavour eq "UNIMARC" ) {
1401 @listtags = ('345', '010');
1402 $subfield="d";
1403 } else {
1404 return;
1407 for my $field ( $record->field(@listtags) ) {
1408 for my $subfield_value ($field->subfield($subfield)){
1409 #check value
1410 $subfield_value = MungeMarcPrice( $subfield_value );
1411 return $subfield_value if ($subfield_value);
1414 return 0; # no price found
1417 =head2 MungeMarcPrice
1419 Return the best guess at what the actual price is from a price field.
1420 =cut
1422 sub MungeMarcPrice {
1423 my ( $price ) = @_;
1425 return unless ( $price =~ m/\d/ ); ## No digits means no price.
1427 ## Look for the currency symbol of the active currency, if it's there,
1428 ## start the price string right after the symbol. This allows us to prefer
1429 ## this native currency price over other currency prices, if possible.
1430 my $active_currency = C4::Context->dbh->selectrow_hashref( 'SELECT * FROM currency WHERE active = 1', {} );
1431 my $symbol = quotemeta( $active_currency->{'symbol'} );
1432 if ( $price =~ m/$symbol/ ) {
1433 my @parts = split(/$symbol/, $price );
1434 $price = $parts[1];
1437 ## Grab the first number in the string ( can use commas or periods for thousands separator and/or decimal separator )
1438 ( $price ) = $price =~ m/([\d\,\.]+[[\,\.]\d\d]?)/;
1440 ## Split price into array on periods and commas
1441 my @parts = split(/[\,\.]/, $price);
1443 ## If the last grouping of digits is more than 2 characters, assume there is no decimal value and put it back.
1444 my $decimal = pop( @parts );
1445 if ( length( $decimal ) > 2 ) {
1446 push( @parts, $decimal );
1447 $decimal = '';
1450 $price = join('', @parts );
1452 if ( $decimal ) {
1453 $price .= ".$decimal";
1456 return $price;
1460 =head2 GetMarcQuantity
1462 return the quantity of a book. Used in acquisition only, when importing a file an iso2709 from a bookseller
1463 Warning : this is not really in the marc standard. In Unimarc, Electre (the most widely used bookseller) use the 969$a
1465 =cut
1467 sub GetMarcQuantity {
1468 my ( $record, $marcflavour ) = @_;
1469 my @listtags;
1470 my $subfield;
1472 if ( $marcflavour eq "MARC21" ) {
1473 return 0
1474 } elsif ( $marcflavour eq "UNIMARC" ) {
1475 @listtags = ('969');
1476 $subfield="a";
1477 } else {
1478 return;
1481 for my $field ( $record->field(@listtags) ) {
1482 for my $subfield_value ($field->subfield($subfield)){
1483 #check value
1484 if ($subfield_value) {
1485 # in France, the cents separator is the , but sometimes, ppl use a .
1486 # in this case, the price will be x100 when unformatted ! Replace the . by a , to get a proper price calculation
1487 $subfield_value =~ s/\./,/ if C4::Context->preference("CurrencyFormat") eq "FR";
1488 return $subfield_value;
1492 return 0; # no price found
1496 =head2 GetAuthorisedValueDesc
1498 my $subfieldvalue =get_authorised_value_desc(
1499 $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category, $opac);
1501 Retrieve the complete description for a given authorised value.
1503 Now takes $category and $value pair too.
1505 my $auth_value_desc =GetAuthorisedValueDesc(
1506 '','', 'DVD' ,'','','CCODE');
1508 If the optional $opac parameter is set to a true value, displays OPAC
1509 descriptions rather than normal ones when they exist.
1511 =cut
1513 sub GetAuthorisedValueDesc {
1514 my ( $tag, $subfield, $value, $framework, $tagslib, $category, $opac ) = @_;
1515 my $dbh = C4::Context->dbh;
1517 if ( !$category ) {
1519 return $value unless defined $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1521 #---- branch
1522 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
1523 return C4::Branch::GetBranchName($value);
1526 #---- itemtypes
1527 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
1528 return getitemtypeinfo($value)->{description};
1531 #---- "true" authorized value
1532 $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
1535 if ( $category ne "" ) {
1536 my $sth = $dbh->prepare( "SELECT lib, lib_opac FROM authorised_values WHERE category = ? AND authorised_value = ?" );
1537 $sth->execute( $category, $value );
1538 my $data = $sth->fetchrow_hashref;
1539 return ( $opac && $data->{'lib_opac'} ) ? $data->{'lib_opac'} : $data->{'lib'};
1540 } else {
1541 return $value; # if nothing is found return the original value
1545 =head2 GetMarcControlnumber
1547 $marccontrolnumber = GetMarcControlnumber($record,$marcflavour);
1549 Get the control number / record Identifier from the MARC record and return it.
1551 =cut
1553 sub GetMarcControlnumber {
1554 my ( $record, $marcflavour ) = @_;
1555 my $controlnumber = "";
1556 # Control number or Record identifier are the same field in MARC21, UNIMARC and NORMARC
1557 # Keep $marcflavour for possible later use
1558 if ($marcflavour eq "MARC21" || $marcflavour eq "UNIMARC" || $marcflavour eq "NORMARC") {
1559 my $controlnumberField = $record->field('001');
1560 if ($controlnumberField) {
1561 $controlnumber = $controlnumberField->data();
1564 return $controlnumber;
1567 =head2 GetMarcISBN
1569 $marcisbnsarray = GetMarcISBN( $record, $marcflavour );
1571 Get all ISBNs from the MARC record and returns them in an array.
1572 ISBNs stored in different fields depending on MARC flavour
1574 =cut
1576 sub GetMarcISBN {
1577 my ( $record, $marcflavour ) = @_;
1578 my $scope;
1579 if ( $marcflavour eq "UNIMARC" ) {
1580 $scope = '010';
1581 } else { # assume marc21 if not unimarc
1582 $scope = '020';
1584 my @marcisbns;
1585 my $isbn = "";
1586 my $tag = "";
1587 my $marcisbn;
1588 foreach my $field ( $record->field($scope) ) {
1589 my $value = $field->as_string();
1590 if ( $isbn ne "" ) {
1591 $marcisbn = { marcisbn => $isbn, };
1592 push @marcisbns, $marcisbn;
1593 $isbn = $value;
1595 if ( $isbn ne $value ) {
1596 $isbn = $isbn . " " . $value;
1600 if ($isbn) {
1601 $marcisbn = { marcisbn => $isbn };
1602 push @marcisbns, $marcisbn; #load last tag into array
1604 return \@marcisbns;
1605 } # end GetMarcISBN
1608 =head2 GetMarcISSN
1610 $marcissnsarray = GetMarcISSN( $record, $marcflavour );
1612 Get all valid ISSNs from the MARC record and returns them in an array.
1613 ISSNs are stored in different fields depending on MARC flavour
1615 =cut
1617 sub GetMarcISSN {
1618 my ( $record, $marcflavour ) = @_;
1619 my $scope;
1620 if ( $marcflavour eq "UNIMARC" ) {
1621 $scope = '011';
1623 else { # assume MARC21 or NORMARC
1624 $scope = '022';
1626 my @marcissns;
1627 foreach my $field ( $record->field($scope) ) {
1628 push @marcissns, $field->subfield( 'a' );
1630 return \@marcissns;
1631 } # end GetMarcISSN
1633 =head2 GetMarcNotes
1635 $marcnotesarray = GetMarcNotes( $record, $marcflavour );
1637 Get all notes from the MARC record and returns them in an array.
1638 The note are stored in different fields depending on MARC flavour
1640 =cut
1642 sub GetMarcNotes {
1643 my ( $record, $marcflavour ) = @_;
1644 my $scope;
1645 if ( $marcflavour eq "UNIMARC" ) {
1646 $scope = '3..';
1647 } else { # assume marc21 if not unimarc
1648 $scope = '5..';
1650 my @marcnotes;
1651 my $note = "";
1652 my $tag = "";
1653 my $marcnote;
1654 foreach my $field ( $record->field($scope) ) {
1655 my $value = $field->as_string();
1656 if ( $note ne "" ) {
1657 $marcnote = { marcnote => $note, };
1658 push @marcnotes, $marcnote;
1659 $note = $value;
1661 if ( $note ne $value ) {
1662 $note = $note . " " . $value;
1666 if ($note) {
1667 $marcnote = { marcnote => $note };
1668 push @marcnotes, $marcnote; #load last tag into array
1670 return \@marcnotes;
1671 } # end GetMarcNotes
1673 =head2 GetMarcSubjects
1675 $marcsubjcts = GetMarcSubjects($record,$marcflavour);
1677 Get all subjects from the MARC record and returns them in an array.
1678 The subjects are stored in different fields depending on MARC flavour
1680 =cut
1682 sub GetMarcSubjects {
1683 my ( $record, $marcflavour ) = @_;
1684 my ( $mintag, $maxtag );
1685 if ( $marcflavour eq "UNIMARC" ) {
1686 $mintag = "600";
1687 $maxtag = "611";
1688 } else { # assume marc21 if not unimarc
1689 $mintag = "600";
1690 $maxtag = "699";
1693 my @marcsubjects;
1694 my $subject = "";
1695 my $subfield = "";
1696 my $marcsubject;
1698 my $subject_limit = C4::Context->preference("TraceCompleteSubfields") ? 'su,complete-subfield' : 'su';
1700 foreach my $field ( $record->field('6..') ) {
1701 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1702 my @subfields_loop;
1703 my @subfields = $field->subfields();
1704 my $counter = 0;
1705 my @link_loop;
1707 # if there is an authority link, build the link with an= subfield9
1708 my $found9 = 0;
1709 for my $subject_subfield (@subfields) {
1711 # don't load unimarc subfields 3,4,5
1712 next if ( ( $marcflavour eq "UNIMARC" ) and ( $subject_subfield->[0] =~ /2|3|4|5/ ) );
1714 # don't load MARC21 subfields 2 (FIXME: any more subfields??)
1715 next if ( ( $marcflavour eq "MARC21" ) and ( $subject_subfield->[0] =~ /2/ ) );
1716 my $code = $subject_subfield->[0];
1717 my $value = $subject_subfield->[1];
1718 my $linkvalue = $value;
1719 $linkvalue =~ s/(\(|\))//g;
1720 my $operator;
1721 if ( $counter != 0 ) {
1722 $operator = ' and ';
1724 if ( $code eq 9 ) {
1725 $found9 = 1;
1726 @link_loop = ( { 'limit' => 'an', link => "$linkvalue" } );
1728 if ( not $found9 ) {
1729 push @link_loop, { 'limit' => $subject_limit, link => $linkvalue, operator => $operator };
1731 my $separator;
1732 if ( $counter != 0 ) {
1733 $separator = C4::Context->preference('authoritysep');
1736 # ignore $9
1737 my @this_link_loop = @link_loop;
1738 push @subfields_loop, { code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator } unless ( $subject_subfield->[0] eq 9 || $subject_subfield->[0] eq '0' );
1739 $counter++;
1742 push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
1745 return \@marcsubjects;
1746 } #end getMARCsubjects
1748 =head2 GetMarcAuthors
1750 authors = GetMarcAuthors($record,$marcflavour);
1752 Get all authors from the MARC record and returns them in an array.
1753 The authors are stored in different fields depending on MARC flavour
1755 =cut
1757 sub GetMarcAuthors {
1758 my ( $record, $marcflavour ) = @_;
1759 my ( $mintag, $maxtag );
1761 # tagslib useful for UNIMARC author reponsabilities
1762 my $tagslib =
1763 &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.
1764 if ( $marcflavour eq "UNIMARC" ) {
1765 $mintag = "700";
1766 $maxtag = "712";
1767 } elsif ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) { # assume marc21 or normarc if not unimarc
1768 $mintag = "700";
1769 $maxtag = "720";
1770 } else {
1771 return;
1773 my @marcauthors;
1775 foreach my $field ( $record->fields ) {
1776 next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
1777 my @subfields_loop;
1778 my @link_loop;
1779 my @subfields = $field->subfields();
1780 my $count_auth = 0;
1782 # if there is an authority link, build the link with Koha-Auth-Number: subfield9
1783 my $subfield9 = $field->subfield('9');
1784 for my $authors_subfield (@subfields) {
1786 # don't load unimarc subfields 3, 5
1787 next if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /3|5/ ) );
1788 my $subfieldcode = $authors_subfield->[0];
1789 my $value = $authors_subfield->[1];
1790 my $linkvalue = $value;
1791 $linkvalue =~ s/(\(|\))//g;
1792 my $operator;
1793 if ( $count_auth != 0 ) {
1794 $operator = ' and ';
1797 # if we have an authority link, use that as the link, otherwise use standard searching
1798 if ($subfield9) {
1799 @link_loop = ( { 'limit' => 'an', link => "$subfield9" } );
1800 } else {
1802 # reset $linkvalue if UNIMARC author responsibility
1803 if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] eq "4" ) ) {
1804 $linkvalue = "(" . GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ) . ")";
1806 push @link_loop, { 'limit' => 'au', link => $linkvalue, operator => $operator };
1808 $value = GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib )
1809 if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /4/ ) );
1810 my @this_link_loop = @link_loop;
1811 my $separator;
1812 if ( $count_auth != 0 ) {
1813 $separator = C4::Context->preference('authoritysep');
1815 push @subfields_loop,
1816 { tag => $field->tag(),
1817 code => $subfieldcode,
1818 value => $value,
1819 link_loop => \@this_link_loop,
1820 separator => $separator
1822 unless ( $authors_subfield->[0] eq '9' || $authors_subfield->[0] eq '0');
1823 $count_auth++;
1825 push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
1827 return \@marcauthors;
1830 =head2 GetMarcUrls
1832 $marcurls = GetMarcUrls($record,$marcflavour);
1834 Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
1835 Assumes web resources (not uncommon in MARC21 to omit resource type ind)
1837 =cut
1839 sub GetMarcUrls {
1840 my ( $record, $marcflavour ) = @_;
1842 my @marcurls;
1843 for my $field ( $record->field('856') ) {
1844 my @notes;
1845 for my $note ( $field->subfield('z') ) {
1846 push @notes, { note => $note };
1848 my @urls = $field->subfield('u');
1849 foreach my $url (@urls) {
1850 my $marcurl;
1851 if ( $marcflavour eq 'MARC21' ) {
1852 my $s3 = $field->subfield('3');
1853 my $link = $field->subfield('y');
1854 unless ( $url =~ /^\w+:/ ) {
1855 if ( $field->indicator(1) eq '7' ) {
1856 $url = $field->subfield('2') . "://" . $url;
1857 } elsif ( $field->indicator(1) eq '1' ) {
1858 $url = 'ftp://' . $url;
1859 } else {
1861 # properly, this should be if ind1=4,
1862 # however we will assume http protocol since we're building a link.
1863 $url = 'http://' . $url;
1867 # TODO handle ind 2 (relationship)
1868 $marcurl = {
1869 MARCURL => $url,
1870 notes => \@notes,
1872 $marcurl->{'linktext'} = $link || $s3 || C4::Context->preference('URLLinkText') || $url;
1873 $marcurl->{'part'} = $s3 if ($link);
1874 $marcurl->{'toc'} = 1 if ( defined($s3) && $s3 =~ /^[Tt]able/ );
1875 } else {
1876 $marcurl->{'linktext'} = $field->subfield('2') || C4::Context->preference('URLLinkText') || $url;
1877 $marcurl->{'MARCURL'} = $url;
1879 push @marcurls, $marcurl;
1882 return \@marcurls;
1885 =head2 GetMarcSeries
1887 $marcseriesarray = GetMarcSeries($record,$marcflavour);
1889 Get all series from the MARC record and returns them in an array.
1890 The series are stored in different fields depending on MARC flavour
1892 =cut
1894 sub GetMarcSeries {
1895 my ( $record, $marcflavour ) = @_;
1896 my ( $mintag, $maxtag );
1897 if ( $marcflavour eq "UNIMARC" ) {
1898 $mintag = "600";
1899 $maxtag = "619";
1900 } else { # assume marc21 if not unimarc
1901 $mintag = "440";
1902 $maxtag = "490";
1905 my @marcseries;
1906 my $subjct = "";
1907 my $subfield = "";
1908 my $marcsubjct;
1910 foreach my $field ( $record->field('440'), $record->field('490') ) {
1911 my @subfields_loop;
1913 #my $value = $field->subfield('a');
1914 #$marcsubjct = {MARCSUBJCT => $value,};
1915 my @subfields = $field->subfields();
1917 #warn "subfields:".join " ", @$subfields;
1918 my $counter = 0;
1919 my @link_loop;
1920 for my $series_subfield (@subfields) {
1921 my $volume_number;
1922 undef $volume_number;
1924 # see if this is an instance of a volume
1925 if ( $series_subfield->[0] eq 'v' ) {
1926 $volume_number = 1;
1929 my $code = $series_subfield->[0];
1930 my $value = $series_subfield->[1];
1931 my $linkvalue = $value;
1932 $linkvalue =~ s/(\(|\))//g;
1933 if ( $counter != 0 ) {
1934 push @link_loop, { link => $linkvalue, operator => ' and ', };
1935 } else {
1936 push @link_loop, { link => $linkvalue, operator => undef, };
1938 my $separator;
1939 if ( $counter != 0 ) {
1940 $separator = C4::Context->preference('authoritysep');
1942 if ($volume_number) {
1943 push @subfields_loop, { volumenum => $value };
1944 } else {
1945 if ( $series_subfield->[0] ne '9' ) {
1946 push @subfields_loop, {
1947 code => $code,
1948 value => $value,
1949 link_loop => \@link_loop,
1950 separator => $separator,
1951 volumenum => $volume_number,
1955 $counter++;
1957 push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
1959 #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
1960 #push @marcsubjcts, $marcsubjct;
1961 #$subjct = $value;
1964 my $marcseriessarray = \@marcseries;
1965 return $marcseriessarray;
1966 } #end getMARCseriess
1968 =head2 GetMarcHosts
1970 $marchostsarray = GetMarcHosts($record,$marcflavour);
1972 Get all host records (773s MARC21, 461 UNIMARC) from the MARC record and returns them in an array.
1974 =cut
1976 sub GetMarcHosts {
1977 my ( $record, $marcflavour ) = @_;
1978 my ( $tag,$title_subf,$bibnumber_subf,$itemnumber_subf);
1979 $marcflavour ||="MARC21";
1980 if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
1981 $tag = "773";
1982 $title_subf = "t";
1983 $bibnumber_subf ="0";
1984 $itemnumber_subf='9';
1986 elsif ($marcflavour eq "UNIMARC") {
1987 $tag = "461";
1988 $title_subf = "t";
1989 $bibnumber_subf ="0";
1990 $itemnumber_subf='9';
1993 my @marchosts;
1995 foreach my $field ( $record->field($tag)) {
1997 my @fields_loop;
1999 my $hostbiblionumber = $field->subfield("$bibnumber_subf");
2000 my $hosttitle = $field->subfield($title_subf);
2001 my $hostitemnumber=$field->subfield($itemnumber_subf);
2002 push @fields_loop, { hostbiblionumber => $hostbiblionumber, hosttitle => $hosttitle, hostitemnumber => $hostitemnumber};
2003 push @marchosts, { MARCHOSTS_FIELDS_LOOP => \@fields_loop };
2006 my $marchostsarray = \@marchosts;
2007 return $marchostsarray;
2010 =head2 GetFrameworkCode
2012 $frameworkcode = GetFrameworkCode( $biblionumber )
2014 =cut
2016 sub GetFrameworkCode {
2017 my ($biblionumber) = @_;
2018 my $dbh = C4::Context->dbh;
2019 my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
2020 $sth->execute($biblionumber);
2021 my ($frameworkcode) = $sth->fetchrow;
2022 return $frameworkcode;
2025 =head2 TransformKohaToMarc
2027 $record = TransformKohaToMarc( $hash )
2029 This function builds partial MARC::Record from a hash
2030 Hash entries can be from biblio or biblioitems.
2032 This function is called in acquisition module, to create a basic catalogue
2033 entry from user entry
2035 =cut
2038 sub TransformKohaToMarc {
2039 my $hash = shift;
2040 my $record = MARC::Record->new();
2041 SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
2042 my $db_to_marc = C4::Context->marcfromkohafield;
2043 while ( my ($name, $value) = each %$hash ) {
2044 next unless my $dtm = $db_to_marc->{''}->{$name};
2045 next unless ( scalar( @$dtm ) );
2046 my ($tag, $letter) = @$dtm;
2047 foreach my $value ( split(/\s?\|\s?/, $value, -1) ) {
2048 if ( my $field = $record->field($tag) ) {
2049 $field->add_subfields( $letter => $value );
2051 else {
2052 $record->insert_fields_ordered( MARC::Field->new(
2053 $tag, " ", " ", $letter => $value ) );
2058 return $record;
2061 =head2 PrepHostMarcField
2063 $hostfield = PrepHostMarcField ( $hostbiblionumber,$hostitemnumber,$marcflavour )
2065 This function returns a host field populated with data from the host record, the field can then be added to an analytical record
2067 =cut
2069 sub PrepHostMarcField {
2070 my ($hostbiblionumber,$hostitemnumber, $marcflavour) = @_;
2071 $marcflavour ||="MARC21";
2073 require C4::Items;
2074 my $hostrecord = GetMarcBiblio($hostbiblionumber);
2075 my $item = C4::Items::GetItem($hostitemnumber);
2077 my $hostmarcfield;
2078 if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
2080 #main entry
2081 my $mainentry;
2082 if ($hostrecord->subfield('100','a')){
2083 $mainentry = $hostrecord->subfield('100','a');
2084 } elsif ($hostrecord->subfield('110','a')){
2085 $mainentry = $hostrecord->subfield('110','a');
2086 } else {
2087 $mainentry = $hostrecord->subfield('111','a');
2090 # qualification info
2091 my $qualinfo;
2092 if (my $field260 = $hostrecord->field('260')){
2093 $qualinfo = $field260->as_string( 'abc' );
2097 #other fields
2098 my $ed = $hostrecord->subfield('250','a');
2099 my $barcode = $item->{'barcode'};
2100 my $title = $hostrecord->subfield('245','a');
2102 # record control number, 001 with 003 and prefix
2103 my $recctrlno;
2104 if ($hostrecord->field('001')){
2105 $recctrlno = $hostrecord->field('001')->data();
2106 if ($hostrecord->field('003')){
2107 $recctrlno = '('.$hostrecord->field('003')->data().')'.$recctrlno;
2111 # issn/isbn
2112 my $issn = $hostrecord->subfield('022','a');
2113 my $isbn = $hostrecord->subfield('020','a');
2116 $hostmarcfield = MARC::Field->new(
2117 773, '0', '',
2118 '0' => $hostbiblionumber,
2119 '9' => $hostitemnumber,
2120 'a' => $mainentry,
2121 'b' => $ed,
2122 'd' => $qualinfo,
2123 'o' => $barcode,
2124 't' => $title,
2125 'w' => $recctrlno,
2126 'x' => $issn,
2127 'z' => $isbn
2129 } elsif ($marcflavour eq "UNIMARC") {
2130 $hostmarcfield = MARC::Field->new(
2131 461, '', '',
2132 '0' => $hostbiblionumber,
2133 't' => $hostrecord->subfield('200','a'),
2134 '9' => $hostitemnumber
2138 return $hostmarcfield;
2141 =head2 TransformHtmlToXml
2143 $xml = TransformHtmlToXml( $tags, $subfields, $values, $indicator,
2144 $ind_tag, $auth_type )
2146 $auth_type contains :
2148 =over
2150 =item - nothing : rebuild a biblio. In UNIMARC the encoding is in 100$a pos 26/27
2152 =item - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
2154 =item - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
2156 =back
2158 =cut
2160 sub TransformHtmlToXml {
2161 my ( $tags, $subfields, $values, $indicator, $ind_tag, $auth_type ) = @_;
2162 my $xml = MARC::File::XML::header('UTF-8');
2163 $xml .= "<record>\n";
2164 $auth_type = C4::Context->preference('marcflavour') unless $auth_type;
2165 MARC::File::XML->default_record_format($auth_type);
2167 # in UNIMARC, field 100 contains the encoding
2168 # check that there is one, otherwise the
2169 # MARC::Record->new_from_xml will fail (and Koha will die)
2170 my $unimarc_and_100_exist = 0;
2171 $unimarc_and_100_exist = 1 if $auth_type eq 'ITEM'; # if we rebuild an item, no need of a 100 field
2172 my $prevvalue;
2173 my $prevtag = -1;
2174 my $first = 1;
2175 my $j = -1;
2176 for ( my $i = 0 ; $i < @$tags ; $i++ ) {
2178 if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and @$tags[$i] eq "100" and @$subfields[$i] eq "a" ) {
2180 # if we have a 100 field and it's values are not correct, skip them.
2181 # if we don't have any valid 100 field, we will create a default one at the end
2182 my $enc = substr( @$values[$i], 26, 2 );
2183 if ( $enc eq '01' or $enc eq '50' or $enc eq '03' ) {
2184 $unimarc_and_100_exist = 1;
2185 } else {
2186 next;
2189 @$values[$i] =~ s/&/&amp;/g;
2190 @$values[$i] =~ s/</&lt;/g;
2191 @$values[$i] =~ s/>/&gt;/g;
2192 @$values[$i] =~ s/"/&quot;/g;
2193 @$values[$i] =~ s/'/&apos;/g;
2195 # if ( !utf8::is_utf8( @$values[$i] ) ) {
2196 # utf8::decode( @$values[$i] );
2198 if ( ( @$tags[$i] ne $prevtag ) ) {
2199 $j++ unless ( @$tags[$i] eq "" );
2200 my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) };
2201 my $indicator2 = eval { substr( @$indicator[$j], 1, 1 ) };
2202 my $ind1 = _default_ind_to_space($indicator1);
2203 my $ind2;
2204 if ( @$indicator[$j] ) {
2205 $ind2 = _default_ind_to_space($indicator2);
2206 } else {
2207 warn "Indicator in @$tags[$i] is empty";
2208 $ind2 = " ";
2210 if ( !$first ) {
2211 $xml .= "</datafield>\n";
2212 if ( ( @$tags[$i] && @$tags[$i] > 10 )
2213 && ( @$values[$i] ne "" ) ) {
2214 $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2215 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2216 $first = 0;
2217 } else {
2218 $first = 1;
2220 } else {
2221 if ( @$values[$i] ne "" ) {
2223 # leader
2224 if ( @$tags[$i] eq "000" ) {
2225 $xml .= "<leader>@$values[$i]</leader>\n";
2226 $first = 1;
2228 # rest of the fixed fields
2229 } elsif ( @$tags[$i] < 10 ) {
2230 $xml .= "<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
2231 $first = 1;
2232 } else {
2233 $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2234 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2235 $first = 0;
2239 } else { # @$tags[$i] eq $prevtag
2240 my $indicator1 = eval { substr( @$indicator[$j], 0, 1 ) };
2241 my $indicator2 = eval { substr( @$indicator[$j], 1, 1 ) };
2242 my $ind1 = _default_ind_to_space($indicator1);
2243 my $ind2;
2244 if ( @$indicator[$j] ) {
2245 $ind2 = _default_ind_to_space($indicator2);
2246 } else {
2247 warn "Indicator in @$tags[$i] is empty";
2248 $ind2 = " ";
2250 if ( @$values[$i] eq "" ) {
2251 } else {
2252 if ($first) {
2253 $xml .= "<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
2254 $first = 0;
2256 $xml .= "<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
2259 $prevtag = @$tags[$i];
2261 $xml .= "</datafield>\n" if $xml =~ m/<datafield/;
2262 if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist ) {
2264 # warn "SETTING 100 for $auth_type";
2265 my $string = strftime( "%Y%m%d", localtime(time) );
2267 # set 50 to position 26 is biblios, 13 if authorities
2268 my $pos = 26;
2269 $pos = 13 if $auth_type eq 'UNIMARCAUTH';
2270 $string = sprintf( "%-*s", 35, $string );
2271 substr( $string, $pos, 6, "50" );
2272 $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
2273 $xml .= "<subfield code=\"a\">$string</subfield>\n";
2274 $xml .= "</datafield>\n";
2276 $xml .= "</record>\n";
2277 $xml .= MARC::File::XML::footer();
2278 return $xml;
2281 =head2 _default_ind_to_space
2283 Passed what should be an indicator returns a space
2284 if its undefined or zero length
2286 =cut
2288 sub _default_ind_to_space {
2289 my $s = shift;
2290 if ( !defined $s || $s eq q{} ) {
2291 return ' ';
2293 return $s;
2296 =head2 TransformHtmlToMarc
2298 L<$record> = TransformHtmlToMarc(L<$cgi>)
2299 L<$cgi> is the CGI object which containts the values for subfields
2301 'tag_010_indicator1_531951' ,
2302 'tag_010_indicator2_531951' ,
2303 'tag_010_code_a_531951_145735' ,
2304 'tag_010_subfield_a_531951_145735' ,
2305 'tag_200_indicator1_873510' ,
2306 'tag_200_indicator2_873510' ,
2307 'tag_200_code_a_873510_673465' ,
2308 'tag_200_subfield_a_873510_673465' ,
2309 'tag_200_code_b_873510_704318' ,
2310 'tag_200_subfield_b_873510_704318' ,
2311 'tag_200_code_e_873510_280822' ,
2312 'tag_200_subfield_e_873510_280822' ,
2313 'tag_200_code_f_873510_110730' ,
2314 'tag_200_subfield_f_873510_110730' ,
2316 L<$record> is the MARC::Record object.
2318 =cut
2320 sub TransformHtmlToMarc {
2321 my $cgi = shift;
2323 my @params = $cgi->param();
2325 # explicitly turn on the UTF-8 flag for all
2326 # 'tag_' parameters to avoid incorrect character
2327 # conversion later on
2328 my $cgi_params = $cgi->Vars;
2329 foreach my $param_name ( keys %$cgi_params ) {
2330 if ( $param_name =~ /^tag_/ ) {
2331 my $param_value = $cgi_params->{$param_name};
2332 if ( utf8::decode($param_value) ) {
2333 $cgi_params->{$param_name} = $param_value;
2336 # FIXME - need to do something if string is not valid UTF-8
2340 # creating a new record
2341 my $record = MARC::Record->new();
2342 my $i = 0;
2343 my @fields;
2344 #FIXME This code assumes that the CGI params will be in the same order as the fields in the template; this is no absolute guarantee!
2345 while ( $params[$i] ) { # browse all CGI params
2346 my $param = $params[$i];
2347 my $newfield = 0;
2349 # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
2350 if ( $param eq 'biblionumber' ) {
2351 my ( $biblionumbertagfield, $biblionumbertagsubfield ) = &GetMarcFromKohaField( "biblio.biblionumber", '' );
2352 if ( $biblionumbertagfield < 10 ) {
2353 $newfield = MARC::Field->new( $biblionumbertagfield, $cgi->param($param), );
2354 } else {
2355 $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => $cgi->param($param), );
2357 push @fields, $newfield if ($newfield);
2358 } elsif ( $param =~ /^tag_(\d*)_indicator1_/ ) { # new field start when having 'input name="..._indicator1_..."
2359 my $tag = $1;
2361 my $ind1 = _default_ind_to_space( substr( $cgi->param($param), 0, 1 ) );
2362 my $ind2 = _default_ind_to_space( substr( $cgi->param( $params[ $i + 1 ] ), 0, 1 ) );
2363 $newfield = 0;
2364 my $j = $i + 2;
2366 if ( $tag < 10 ) { # no code for theses fields
2367 # in MARC editor, 000 contains the leader.
2368 if ( $tag eq '000' ) {
2369 # Force a fake leader even if not provided to avoid crashing
2370 # during decoding MARC record containing UTF-8 characters
2371 $record->leader(
2372 length( $cgi->param($params[$j+1]) ) == 24
2373 ? $cgi->param( $params[ $j + 1 ] )
2374 : ' nam a22 4500'
2377 # between 001 and 009 (included)
2378 } elsif ( $cgi->param( $params[ $j + 1 ] ) ne '' ) {
2379 $newfield = MARC::Field->new( $tag, $cgi->param( $params[ $j + 1 ] ), );
2382 # > 009, deal with subfields
2383 } else {
2384 # browse subfields for this tag (reason for _code_ match)
2385 while(defined $params[$j] && $params[$j] =~ /_code_/) {
2386 last unless defined $params[$j+1];
2387 #if next param ne subfield, then it was probably empty
2388 #try next param by incrementing j
2389 if($params[$j+1]!~/_subfield_/) {$j++; next; }
2390 my $fval= $cgi->param($params[$j+1]);
2391 #check if subfield value not empty and field exists
2392 if($fval ne '' && $newfield) {
2393 $newfield->add_subfields( $cgi->param($params[$j]) => $fval);
2395 elsif($fval ne '') {
2396 $newfield = MARC::Field->new( $tag, $ind1, $ind2, $cgi->param($params[$j]) => $fval );
2398 $j += 2;
2399 } #end-of-while
2400 $i= $j-1; #update i for outer loop accordingly
2402 push @fields, $newfield if ($newfield);
2404 $i++;
2407 $record->append_fields(@fields);
2408 return $record;
2411 # cache inverted MARC field map
2412 our $inverted_field_map;
2414 =head2 TransformMarcToKoha
2416 $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
2418 Extract data from a MARC bib record into a hashref representing
2419 Koha biblio, biblioitems, and items fields.
2421 =cut
2423 sub TransformMarcToKoha {
2424 my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
2426 my $result;
2427 $limit_table = $limit_table || 0;
2428 $frameworkcode = '' unless defined $frameworkcode;
2430 unless ( defined $inverted_field_map ) {
2431 $inverted_field_map = _get_inverted_marc_field_map();
2434 my %tables = ();
2435 if ( defined $limit_table && $limit_table eq 'items' ) {
2436 $tables{'items'} = 1;
2437 } else {
2438 $tables{'items'} = 1;
2439 $tables{'biblio'} = 1;
2440 $tables{'biblioitems'} = 1;
2443 # traverse through record
2444 MARCFIELD: foreach my $field ( $record->fields() ) {
2445 my $tag = $field->tag();
2446 next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
2447 if ( $field->is_control_field() ) {
2448 my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
2449 ENTRY: foreach my $entry ( @{$kohafields} ) {
2450 my ( $subfield, $table, $column ) = @{$entry};
2451 next ENTRY unless exists $tables{$table};
2452 my $key = _disambiguate( $table, $column );
2453 if ( $result->{$key} ) {
2454 unless ( ( $key eq "biblionumber" or $key eq "biblioitemnumber" ) and ( $field->data() eq "" ) ) {
2455 $result->{$key} .= " | " . $field->data();
2457 } else {
2458 $result->{$key} = $field->data();
2461 } else {
2463 # deal with subfields
2464 MARCSUBFIELD: foreach my $sf ( $field->subfields() ) {
2465 my $code = $sf->[0];
2466 next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
2467 my $value = $sf->[1];
2468 SFENTRY: foreach my $entry ( @{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} } ) {
2469 my ( $table, $column ) = @{$entry};
2470 next SFENTRY unless exists $tables{$table};
2471 my $key = _disambiguate( $table, $column );
2472 if ( $result->{$key} ) {
2473 unless ( ( $key eq "biblionumber" or $key eq "biblioitemnumber" ) and ( $value eq "" ) ) {
2474 $result->{$key} .= " | " . $value;
2476 } else {
2477 $result->{$key} = $value;
2484 # modify copyrightdate to keep only the 1st year found
2485 if ( exists $result->{'copyrightdate'} ) {
2486 my $temp = $result->{'copyrightdate'};
2487 $temp =~ m/c(\d\d\d\d)/;
2488 if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
2489 $result->{'copyrightdate'} = $1;
2490 } else { # if no cYYYY, get the 1st date.
2491 $temp =~ m/(\d\d\d\d)/;
2492 $result->{'copyrightdate'} = $1;
2496 # modify publicationyear to keep only the 1st year found
2497 if ( exists $result->{'publicationyear'} ) {
2498 my $temp = $result->{'publicationyear'};
2499 if ( $temp =~ m/c(\d\d\d\d)/ and $1 > 0 ) { # search cYYYY first
2500 $result->{'publicationyear'} = $1;
2501 } else { # if no cYYYY, get the 1st date.
2502 $temp =~ m/(\d\d\d\d)/;
2503 $result->{'publicationyear'} = $1;
2507 return $result;
2510 sub _get_inverted_marc_field_map {
2511 my $field_map = {};
2512 my $relations = C4::Context->marcfromkohafield;
2514 foreach my $frameworkcode ( keys %{$relations} ) {
2515 foreach my $kohafield ( keys %{ $relations->{$frameworkcode} } ) {
2516 next unless @{ $relations->{$frameworkcode}->{$kohafield} }; # not all columns are mapped to MARC tag & subfield
2517 my $tag = $relations->{$frameworkcode}->{$kohafield}->[0];
2518 my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
2519 my ( $table, $column ) = split /[.]/, $kohafield, 2;
2520 push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
2521 push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
2524 return $field_map;
2527 =head2 _disambiguate
2529 $newkey = _disambiguate($table, $field);
2531 This is a temporary hack to distinguish between the
2532 following sets of columns when using TransformMarcToKoha.
2534 items.cn_source & biblioitems.cn_source
2535 items.cn_sort & biblioitems.cn_sort
2537 Columns that are currently NOT distinguished (FIXME
2538 due to lack of time to fully test) are:
2540 biblio.notes and biblioitems.notes
2541 biblionumber
2542 timestamp
2543 biblioitemnumber
2545 FIXME - this is necessary because prefixing each column
2546 name with the table name would require changing lots
2547 of code and templates, and exposing more of the DB
2548 structure than is good to the UI templates, particularly
2549 since biblio and bibloitems may well merge in a future
2550 version. In the future, it would also be good to
2551 separate DB access and UI presentation field names
2552 more.
2554 =cut
2556 sub CountItemsIssued {
2557 my ($biblionumber) = @_;
2558 my $dbh = C4::Context->dbh;
2559 my $sth = $dbh->prepare('SELECT COUNT(*) as issuedCount FROM items, issues WHERE items.itemnumber = issues.itemnumber AND items.biblionumber = ?');
2560 $sth->execute($biblionumber);
2561 my $row = $sth->fetchrow_hashref();
2562 return $row->{'issuedCount'};
2565 sub _disambiguate {
2566 my ( $table, $column ) = @_;
2567 if ( $column eq "cn_sort" or $column eq "cn_source" ) {
2568 return $table . '.' . $column;
2569 } else {
2570 return $column;
2575 =head2 get_koha_field_from_marc
2577 $result->{_disambiguate($table, $field)} =
2578 get_koha_field_from_marc($table,$field,$record,$frameworkcode);
2580 Internal function to map data from the MARC record to a specific non-MARC field.
2581 FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
2583 =cut
2585 sub get_koha_field_from_marc {
2586 my ( $koha_table, $koha_column, $record, $frameworkcode ) = @_;
2587 my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table . '.' . $koha_column, $frameworkcode );
2588 my $kohafield;
2589 foreach my $field ( $record->field($tagfield) ) {
2590 if ( $field->tag() < 10 ) {
2591 if ($kohafield) {
2592 $kohafield .= " | " . $field->data();
2593 } else {
2594 $kohafield = $field->data();
2596 } else {
2597 if ( $field->subfields ) {
2598 my @subfields = $field->subfields();
2599 foreach my $subfieldcount ( 0 .. $#subfields ) {
2600 if ( $subfields[$subfieldcount][0] eq $subfield ) {
2601 if ($kohafield) {
2602 $kohafield .= " | " . $subfields[$subfieldcount][1];
2603 } else {
2604 $kohafield = $subfields[$subfieldcount][1];
2611 return $kohafield;
2614 =head2 TransformMarcToKohaOneField
2616 $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )
2618 =cut
2620 sub TransformMarcToKohaOneField {
2622 # FIXME ? if a field has a repeatable subfield that is used in old-db,
2623 # only the 1st will be retrieved...
2624 my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
2625 my $res = "";
2626 my ( $tagfield, $subfield ) = GetMarcFromKohaField( $kohatable . "." . $kohafield, $frameworkcode );
2627 foreach my $field ( $record->field($tagfield) ) {
2628 if ( $field->tag() < 10 ) {
2629 if ( $result->{$kohafield} ) {
2630 $result->{$kohafield} .= " | " . $field->data();
2631 } else {
2632 $result->{$kohafield} = $field->data();
2634 } else {
2635 if ( $field->subfields ) {
2636 my @subfields = $field->subfields();
2637 foreach my $subfieldcount ( 0 .. $#subfields ) {
2638 if ( $subfields[$subfieldcount][0] eq $subfield ) {
2639 if ( $result->{$kohafield} ) {
2640 $result->{$kohafield} .= " | " . $subfields[$subfieldcount][1];
2641 } else {
2642 $result->{$kohafield} = $subfields[$subfieldcount][1];
2649 return $result;
2656 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2657 # at the same time
2658 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2659 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2660 # =head2 ModZebrafiles
2662 # &ModZebrafiles( $dbh, $biblionumber, $record, $folder, $server );
2664 # =cut
2666 # sub ModZebrafiles {
2668 # my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;
2670 # my $op;
2671 # my $zebradir =
2672 # C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
2673 # unless ( opendir( DIR, "$zebradir" ) ) {
2674 # warn "$zebradir not found";
2675 # return;
2677 # closedir DIR;
2678 # my $filename = $zebradir . $biblionumber;
2680 # if ($record) {
2681 # open( OUTPUT, ">", $filename . ".xml" );
2682 # print OUTPUT $record;
2683 # close OUTPUT;
2687 =head2 ModZebra
2689 ModZebra( $biblionumber, $op, $server, $oldRecord, $newRecord );
2691 $biblionumber is the biblionumber we want to index
2693 $op is specialUpdate or delete, and is used to know what we want to do
2695 $server is the server that we want to update
2697 $oldRecord is the MARC::Record containing the previous version of the record. This is used only when
2698 NoZebra=1, as NoZebra indexing needs to know the previous version of a record in order to
2699 do an update.
2701 $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.
2703 =cut
2705 sub ModZebra {
2706 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2707 my ( $biblionumber, $op, $server, $oldRecord, $newRecord ) = @_;
2708 my $dbh = C4::Context->dbh;
2710 # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2711 # at the same time
2712 # replaced by a zebraqueue table, that is filled with ModZebra to run.
2713 # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
2715 if ( C4::Context->preference("NoZebra") ) {
2717 # lock the nozebra table : we will read index lines, update them in Perl process
2718 # and write everything in 1 transaction.
2719 # lock the table to avoid someone else overwriting what we are doing
2720 $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE, auth_subfield_structure READ');
2721 my %result; # the result hash that will be built by deletion / add, and written on mySQL at the end, to improve speed
2722 if ( $op eq 'specialUpdate' ) {
2724 # OK, we have to add or update the record
2725 # 1st delete (virtually, in indexes), if record actually exists
2726 if ($oldRecord) {
2727 %result = _DelBiblioNoZebra( $biblionumber, $oldRecord, $server );
2730 # ... add the record
2731 %result = _AddBiblioNoZebra( $biblionumber, $newRecord, $server, %result );
2732 } else {
2734 # it's a deletion, delete the record...
2735 # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
2736 %result = _DelBiblioNoZebra( $biblionumber, $oldRecord, $server );
2739 # ok, now update the database...
2740 my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
2741 foreach my $key ( keys %result ) {
2742 foreach my $index ( keys %{ $result{$key} } ) {
2743 $sth->execute( $result{$key}->{$index}, $server, $key, $index );
2746 $dbh->do('UNLOCK TABLES');
2747 } else {
2750 # we use zebra, just fill zebraqueue table
2752 my $check_sql = "SELECT COUNT(*) FROM zebraqueue
2753 WHERE server = ?
2754 AND biblio_auth_number = ?
2755 AND operation = ?
2756 AND done = 0";
2757 my $check_sth = $dbh->prepare_cached($check_sql);
2758 $check_sth->execute( $server, $biblionumber, $op );
2759 my ($count) = $check_sth->fetchrow_array;
2760 $check_sth->finish();
2761 if ( $count == 0 ) {
2762 my $sth = $dbh->prepare("INSERT INTO zebraqueue (biblio_auth_number,server,operation) VALUES(?,?,?)");
2763 $sth->execute( $biblionumber, $server, $op );
2764 $sth->finish;
2769 =head2 GetNoZebraIndexes
2771 %indexes = GetNoZebraIndexes;
2773 return the data from NoZebraIndexes syspref.
2775 =cut
2777 sub GetNoZebraIndexes {
2778 my $no_zebra_indexes = C4::Context->preference('NoZebraIndexes');
2779 my %indexes;
2780 INDEX: foreach my $line ( split /['"],[\n\r]*/, $no_zebra_indexes ) {
2781 $line =~ /(.*)=>(.*)/;
2782 my $index = $1; # initial ' or " is removed afterwards
2783 my $fields = $2;
2784 $index =~ s/'|"|\s//g;
2785 $fields =~ s/'|"|\s//g;
2786 $indexes{$index} = $fields;
2788 return %indexes;
2791 =head2 EmbedItemsInMarcBiblio
2793 EmbedItemsInMarcBiblio($marc, $biblionumber);
2795 Given a MARC::Record object containing a bib record,
2796 modify it to include the items attached to it as 9XX
2797 per the bib's MARC framework.
2799 =cut
2801 sub EmbedItemsInMarcBiblio {
2802 my ($marc, $biblionumber) = @_;
2803 croak "No MARC record" unless $marc;
2805 my $frameworkcode = GetFrameworkCode($biblionumber);
2806 _strip_item_fields($marc, $frameworkcode);
2808 # ... and embed the current items
2809 my $dbh = C4::Context->dbh;
2810 my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
2811 $sth->execute($biblionumber);
2812 my @item_fields;
2813 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2814 while (my ($itemnumber) = $sth->fetchrow_array) {
2815 require C4::Items;
2816 my $item_marc = C4::Items::GetMarcItem($biblionumber, $itemnumber);
2817 push @item_fields, $item_marc->field($itemtag);
2819 $marc->append_fields(@item_fields);
2822 =head1 INTERNAL FUNCTIONS
2824 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
2826 function to delete a biblio in NoZebra indexes
2827 This function does NOT delete anything in database : it reads all the indexes entries
2828 that have to be deleted & delete them in the hash
2830 The SQL part is done either :
2831 - after the Add if we are modifying a biblio (delete + add again)
2832 - immediatly after this sub if we are doing a true deletion.
2834 $server can be 'biblioserver' or 'authorityserver' : it indexes biblios or authorities (in the same table, $server being part of the table itself
2836 =cut
2838 sub _DelBiblioNoZebra {
2839 my ( $biblionumber, $record, $server ) = @_;
2841 # Get the indexes
2842 my $dbh = C4::Context->dbh;
2844 # Get the indexes
2845 my %index;
2846 my $title;
2847 if ( $server eq 'biblioserver' ) {
2848 %index = GetNoZebraIndexes;
2850 # get title of the record (to store the 10 first letters with the index)
2851 my ( $titletag, $titlesubfield ) = GetMarcFromKohaField( 'biblio.title', '' ); # FIXME: should be GetFrameworkCode($biblionumber) ??
2852 $title = lc( $record->subfield( $titletag, $titlesubfield ) );
2853 } else {
2855 # for authorities, the "title" is the $a mainentry
2856 my ( $auth_type_tag, $auth_type_sf ) = C4::AuthoritiesMarc::get_auth_type_location();
2857 my $authref = C4::AuthoritiesMarc::GetAuthType( $record->subfield( $auth_type_tag, $auth_type_sf ) );
2858 warn "ERROR : authtype undefined for " . $record->as_formatted unless $authref;
2859 $title = $record->subfield( $authref->{auth_tag_to_report}, 'a' );
2860 $index{'mainmainentry'} = $authref->{'auth_tag_to_report'} . 'a';
2861 $index{'mainentry'} = $authref->{'auth_tag_to_report'} . '*';
2862 $index{'auth_type'} = "${auth_type_tag}${auth_type_sf}";
2865 my %result;
2867 # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2868 $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
2870 # limit to 10 char, should be enough, and limit the DB size
2871 $title = substr( $title, 0, 10 );
2873 #parse each field
2874 my $sth2 = $dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2875 foreach my $field ( $record->fields() ) {
2877 #parse each subfield
2878 next if $field->tag < 10;
2879 foreach my $subfield ( $field->subfields() ) {
2880 my $tag = $field->tag();
2881 my $subfieldcode = $subfield->[0];
2882 my $indexed = 0;
2884 # check each index to see if the subfield is stored somewhere
2885 # otherwise, store it in __RAW__ index
2886 foreach my $key ( keys %index ) {
2888 # warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
2889 if ( $index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/ ) {
2890 $indexed = 1;
2891 my $line = lc $subfield->[1];
2893 # remove meaningless value in the field...
2894 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2896 # ... and split in words
2897 foreach ( split / /, $line ) {
2898 next unless $_; # skip empty values (multiple spaces)
2899 # if the entry is already here, do nothing, the biblionumber has already be removed
2900 unless ( defined( $result{$key}->{$_} ) && ( $result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/ ) ) {
2902 # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2903 $sth2->execute( $server, $key, $_ );
2904 my $existing_biblionumbers = $sth2->fetchrow;
2906 # it exists
2907 if ($existing_biblionumbers) {
2909 # warn " existing for $key $_: $existing_biblionumbers";
2910 $result{$key}->{$_} = $existing_biblionumbers;
2911 $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2918 # the subfield is not indexed, store it in __RAW__ index anyway
2919 unless ($indexed) {
2920 my $line = lc $subfield->[1];
2921 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
2923 # ... and split in words
2924 foreach ( split / /, $line ) {
2925 next unless $_; # skip empty values (multiple spaces)
2926 # if the entry is already here, do nothing, the biblionumber has already be removed
2927 unless ( $result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/ ) {
2929 # get the index value if it exist in the nozebra table and remove the entry, otherwise, do nothing
2930 $sth2->execute( $server, '__RAW__', $_ );
2931 my $existing_biblionumbers = $sth2->fetchrow;
2933 # it exists
2934 if ($existing_biblionumbers) {
2935 $result{'__RAW__'}->{$_} = $existing_biblionumbers;
2936 $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
2943 return %result;
2946 =head2 _AddBiblioNoZebra
2948 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
2950 function to add a biblio in NoZebra indexes
2952 =cut
2954 sub _AddBiblioNoZebra {
2955 my ( $biblionumber, $record, $server, %result ) = @_;
2956 my $dbh = C4::Context->dbh;
2958 # Get the indexes
2959 my %index;
2960 my $title;
2961 if ( $server eq 'biblioserver' ) {
2962 %index = GetNoZebraIndexes;
2964 # get title of the record (to store the 10 first letters with the index)
2965 my ( $titletag, $titlesubfield ) = GetMarcFromKohaField( 'biblio.title', '' ); # FIXME: should be GetFrameworkCode($biblionumber) ??
2966 $title = lc( $record->subfield( $titletag, $titlesubfield ) );
2967 } else {
2969 # warn "server : $server";
2970 # for authorities, the "title" is the $a mainentry
2971 my ( $auth_type_tag, $auth_type_sf ) = C4::AuthoritiesMarc::get_auth_type_location();
2972 my $authref = C4::AuthoritiesMarc::GetAuthType( $record->subfield( $auth_type_tag, $auth_type_sf ) );
2973 warn "ERROR : authtype undefined for " . $record->as_formatted unless $authref;
2974 $title = $record->subfield( $authref->{auth_tag_to_report}, 'a' );
2975 $index{'mainmainentry'} = $authref->{auth_tag_to_report} . 'a';
2976 $index{'mainentry'} = $authref->{auth_tag_to_report} . '*';
2977 $index{'auth_type'} = "${auth_type_tag}${auth_type_sf}";
2980 # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
2981 $title =~ s/ |\.|,|;|\[|\]|\(|\)|\*|-|'|:|=|\r|\n//g;
2983 # limit to 10 char, should be enough, and limit the DB size
2984 $title = substr( $title, 0, 10 );
2986 #parse each field
2987 my $sth2 = $dbh->prepare('SELECT biblionumbers FROM nozebra WHERE server=? AND indexname=? AND value=?');
2988 foreach my $field ( $record->fields() ) {
2990 #parse each subfield
2991 ###FIXME: impossible to index a 001-009 value with NoZebra
2992 next if $field->tag < 10;
2993 foreach my $subfield ( $field->subfields() ) {
2994 my $tag = $field->tag();
2995 my $subfieldcode = $subfield->[0];
2996 my $indexed = 0;
2998 # warn "INDEXING :".$subfield->[1];
2999 # check each index to see if the subfield is stored somewhere
3000 # otherwise, store it in __RAW__ index
3001 foreach my $key ( keys %index ) {
3003 # warn "examining $key index : ".$index{$key}." for $tag $subfieldcode";
3004 if ( $index{$key} =~ /$tag\*/ or $index{$key} =~ /$tag$subfieldcode/ ) {
3005 $indexed = 1;
3006 my $line = lc $subfield->[1];
3008 # remove meaningless value in the field...
3009 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
3011 # ... and split in words
3012 foreach ( split / /, $line ) {
3013 next unless $_; # skip empty values (multiple spaces)
3014 # if the entry is already here, improve weight
3016 # warn "managing $_";
3017 if ( exists $result{$key}->{$_} && $result{$key}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d+);/ ) {
3018 my $weight = $1 + 1;
3019 $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
3020 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
3021 } else {
3023 # get the value if it exist in the nozebra table, otherwise, create it
3024 $sth2->execute( $server, $key, $_ );
3025 my $existing_biblionumbers = $sth2->fetchrow;
3027 # it exists
3028 if ($existing_biblionumbers) {
3029 $result{$key}->{"$_"} = $existing_biblionumbers;
3030 my $weight = defined $1 ? $1 + 1 : 1;
3031 $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//g;
3032 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
3034 # create a new ligne for this entry
3035 } else {
3037 # warn "INSERT : $server / $key / $_";
3038 $dbh->do( 'INSERT INTO nozebra SET server=' . $dbh->quote($server) . ', indexname=' . $dbh->quote($key) . ',value=' . $dbh->quote($_) );
3039 $result{$key}->{"$_"} .= "$biblionumber,$title-1;";
3046 # the subfield is not indexed, store it in __RAW__ index anyway
3047 unless ($indexed) {
3048 my $line = lc $subfield->[1];
3049 $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
3051 # ... and split in words
3052 foreach ( split / /, $line ) {
3053 next unless $_; # skip empty values (multiple spaces)
3054 # if the entry is already here, improve weight
3055 my $tmpstr = $result{'__RAW__'}->{"$_"} || "";
3056 if ( $tmpstr =~ /$biblionumber,\Q$title\E\-(\d+);/ ) {
3057 my $weight = $1 + 1;
3058 $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
3059 $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
3060 } else {
3062 # get the value if it exist in the nozebra table, otherwise, create it
3063 $sth2->execute( $server, '__RAW__', $_ );
3064 my $existing_biblionumbers = $sth2->fetchrow;
3066 # it exists
3067 if ($existing_biblionumbers) {
3068 $result{'__RAW__'}->{"$_"} = $existing_biblionumbers;
3069 my $weight = ( $1 ? $1 : 0 ) + 1;
3070 $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d+);//;
3071 $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
3073 # create a new ligne for this entry
3074 } else {
3075 $dbh->do( 'INSERT INTO nozebra SET server=' . $dbh->quote($server) . ', indexname="__RAW__",value=' . $dbh->quote($_) );
3076 $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-1;";
3083 return %result;
3086 =head2 _koha_marc_update_bib_ids
3089 _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
3091 Internal function to add or update biblionumber and biblioitemnumber to
3092 the MARC XML.
3094 =cut
3096 sub _koha_marc_update_bib_ids {
3097 my ( $record, $frameworkcode, $biblionumber, $biblioitemnumber ) = @_;
3099 # we must add bibnum and bibitemnum in MARC::Record...
3100 # we build the new field with biblionumber and biblioitemnumber
3101 # we drop the original field
3102 # we add the new builded field.
3103 my ( $biblio_tag, $biblio_subfield ) = GetMarcFromKohaField( "biblio.biblionumber", $frameworkcode );
3104 die qq{No biblionumber tag for framework "$frameworkcode"} unless $biblio_tag;
3105 my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber", $frameworkcode );
3106 die qq{No biblioitemnumber tag for framework "$frameworkcode"} unless $biblio_tag;
3108 if ( $biblio_tag == $biblioitem_tag ) {
3110 # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
3111 my $new_field = MARC::Field->new(
3112 $biblio_tag, '', '',
3113 "$biblio_subfield" => $biblionumber,
3114 "$biblioitem_subfield" => $biblioitemnumber
3117 # drop old field and create new one...
3118 my $old_field = $record->field($biblio_tag);
3119 $record->delete_field($old_field) if $old_field;
3120 $record->insert_fields_ordered($new_field);
3121 } else {
3123 # biblionumber & biblioitemnumber are in different fields
3125 # deal with biblionumber
3126 my ( $new_field, $old_field );
3127 if ( $biblio_tag < 10 ) {
3128 $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
3129 } else {
3130 $new_field = MARC::Field->new( $biblio_tag, '', '', "$biblio_subfield" => $biblionumber );
3133 # drop old field and create new one...
3134 $old_field = $record->field($biblio_tag);
3135 $record->delete_field($old_field) if $old_field;
3136 $record->insert_fields_ordered($new_field);
3138 # deal with biblioitemnumber
3139 if ( $biblioitem_tag < 10 ) {
3140 $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
3141 } else {
3142 $new_field = MARC::Field->new( $biblioitem_tag, '', '', "$biblioitem_subfield" => $biblioitemnumber, );
3145 # drop old field and create new one...
3146 $old_field = $record->field($biblioitem_tag);
3147 $record->delete_field($old_field) if $old_field;
3148 $record->insert_fields_ordered($new_field);
3152 =head2 _koha_marc_update_biblioitem_cn_sort
3154 _koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
3156 Given a MARC bib record and the biblioitem hash, update the
3157 subfield that contains a copy of the value of biblioitems.cn_sort.
3159 =cut
3161 sub _koha_marc_update_biblioitem_cn_sort {
3162 my $marc = shift;
3163 my $biblioitem = shift;
3164 my $frameworkcode = shift;
3166 my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.cn_sort", $frameworkcode );
3167 return unless $biblioitem_tag;
3169 my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3171 if ( my $field = $marc->field($biblioitem_tag) ) {
3172 $field->delete_subfield( code => $biblioitem_subfield );
3173 if ( $cn_sort ne '' ) {
3174 $field->add_subfields( $biblioitem_subfield => $cn_sort );
3176 } else {
3178 # if we get here, no biblioitem tag is present in the MARC record, so
3179 # we'll create it if $cn_sort is not empty -- this would be
3180 # an odd combination of events, however
3181 if ($cn_sort) {
3182 $marc->insert_grouped_field( MARC::Field->new( $biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort ) );
3187 =head2 _koha_add_biblio
3189 my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
3191 Internal function to add a biblio ($biblio is a hash with the values)
3193 =cut
3195 sub _koha_add_biblio {
3196 my ( $dbh, $biblio, $frameworkcode ) = @_;
3198 my $error;
3200 # set the series flag
3201 unless (defined $biblio->{'serial'}){
3202 $biblio->{'serial'} = 0;
3203 if ( $biblio->{'seriestitle'} ) { $biblio->{'serial'} = 1 }
3206 my $query = "INSERT INTO biblio
3207 SET frameworkcode = ?,
3208 author = ?,
3209 title = ?,
3210 unititle =?,
3211 notes = ?,
3212 serial = ?,
3213 seriestitle = ?,
3214 copyrightdate = ?,
3215 datecreated=NOW(),
3216 abstract = ?
3218 my $sth = $dbh->prepare($query);
3219 $sth->execute(
3220 $frameworkcode, $biblio->{'author'}, $biblio->{'title'}, $biblio->{'unititle'}, $biblio->{'notes'},
3221 $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}
3224 my $biblionumber = $dbh->{'mysql_insertid'};
3225 if ( $dbh->errstr ) {
3226 $error .= "ERROR in _koha_add_biblio $query" . $dbh->errstr;
3227 warn $error;
3230 $sth->finish();
3232 #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
3233 return ( $biblionumber, $error );
3236 =head2 _koha_modify_biblio
3238 my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
3240 Internal function for updating the biblio table
3242 =cut
3244 sub _koha_modify_biblio {
3245 my ( $dbh, $biblio, $frameworkcode ) = @_;
3246 my $error;
3248 my $query = "
3249 UPDATE biblio
3250 SET frameworkcode = ?,
3251 author = ?,
3252 title = ?,
3253 unititle = ?,
3254 notes = ?,
3255 serial = ?,
3256 seriestitle = ?,
3257 copyrightdate = ?,
3258 abstract = ?
3259 WHERE biblionumber = ?
3262 my $sth = $dbh->prepare($query);
3264 $sth->execute(
3265 $frameworkcode, $biblio->{'author'}, $biblio->{'title'}, $biblio->{'unititle'}, $biblio->{'notes'},
3266 $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}, $biblio->{'biblionumber'}
3267 ) if $biblio->{'biblionumber'};
3269 if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
3270 $error .= "ERROR in _koha_modify_biblio $query" . $dbh->errstr;
3271 warn $error;
3273 return ( $biblio->{'biblionumber'}, $error );
3276 =head2 _koha_modify_biblioitem_nonmarc
3278 my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
3280 Updates biblioitems row except for marc and marcxml, which should be changed
3281 via ModBiblioMarc
3283 =cut
3285 sub _koha_modify_biblioitem_nonmarc {
3286 my ( $dbh, $biblioitem ) = @_;
3287 my $error;
3289 # re-calculate the cn_sort, it may have changed
3290 my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3292 my $query = "UPDATE biblioitems
3293 SET biblionumber = ?,
3294 volume = ?,
3295 number = ?,
3296 itemtype = ?,
3297 isbn = ?,
3298 issn = ?,
3299 publicationyear = ?,
3300 publishercode = ?,
3301 volumedate = ?,
3302 volumedesc = ?,
3303 collectiontitle = ?,
3304 collectionissn = ?,
3305 collectionvolume= ?,
3306 editionstatement= ?,
3307 editionresponsibility = ?,
3308 illus = ?,
3309 pages = ?,
3310 notes = ?,
3311 size = ?,
3312 place = ?,
3313 lccn = ?,
3314 url = ?,
3315 cn_source = ?,
3316 cn_class = ?,
3317 cn_item = ?,
3318 cn_suffix = ?,
3319 cn_sort = ?,
3320 totalissues = ?,
3321 ean = ?
3322 where biblioitemnumber = ?
3324 my $sth = $dbh->prepare($query);
3325 $sth->execute(
3326 $biblioitem->{'biblionumber'}, $biblioitem->{'volume'}, $biblioitem->{'number'}, $biblioitem->{'itemtype'},
3327 $biblioitem->{'isbn'}, $biblioitem->{'issn'}, $biblioitem->{'publicationyear'}, $biblioitem->{'publishercode'},
3328 $biblioitem->{'volumedate'}, $biblioitem->{'volumedesc'}, $biblioitem->{'collectiontitle'}, $biblioitem->{'collectionissn'},
3329 $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
3330 $biblioitem->{'pages'}, $biblioitem->{'bnotes'}, $biblioitem->{'size'}, $biblioitem->{'place'},
3331 $biblioitem->{'lccn'}, $biblioitem->{'url'}, $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'},
3332 $biblioitem->{'cn_item'}, $biblioitem->{'cn_suffix'}, $cn_sort, $biblioitem->{'totalissues'},
3333 $biblioitem->{'ean'},
3334 $biblioitem->{'biblioitemnumber'}
3336 if ( $dbh->errstr ) {
3337 $error .= "ERROR in _koha_modify_biblioitem_nonmarc $query" . $dbh->errstr;
3338 warn $error;
3340 return ( $biblioitem->{'biblioitemnumber'}, $error );
3343 =head2 _koha_add_biblioitem
3345 my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
3347 Internal function to add a biblioitem
3349 =cut
3351 sub _koha_add_biblioitem {
3352 my ( $dbh, $biblioitem ) = @_;
3353 my $error;
3355 my ($cn_sort) = GetClassSort( $biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
3356 my $query = "INSERT INTO biblioitems SET
3357 biblionumber = ?,
3358 volume = ?,
3359 number = ?,
3360 itemtype = ?,
3361 isbn = ?,
3362 issn = ?,
3363 publicationyear = ?,
3364 publishercode = ?,
3365 volumedate = ?,
3366 volumedesc = ?,
3367 collectiontitle = ?,
3368 collectionissn = ?,
3369 collectionvolume= ?,
3370 editionstatement= ?,
3371 editionresponsibility = ?,
3372 illus = ?,
3373 pages = ?,
3374 notes = ?,
3375 size = ?,
3376 place = ?,
3377 lccn = ?,
3378 marc = ?,
3379 url = ?,
3380 cn_source = ?,
3381 cn_class = ?,
3382 cn_item = ?,
3383 cn_suffix = ?,
3384 cn_sort = ?,
3385 totalissues = ?,
3386 ean = ?
3388 my $sth = $dbh->prepare($query);
3389 $sth->execute(
3390 $biblioitem->{'biblionumber'}, $biblioitem->{'volume'}, $biblioitem->{'number'}, $biblioitem->{'itemtype'},
3391 $biblioitem->{'isbn'}, $biblioitem->{'issn'}, $biblioitem->{'publicationyear'}, $biblioitem->{'publishercode'},
3392 $biblioitem->{'volumedate'}, $biblioitem->{'volumedesc'}, $biblioitem->{'collectiontitle'}, $biblioitem->{'collectionissn'},
3393 $biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
3394 $biblioitem->{'pages'}, $biblioitem->{'bnotes'}, $biblioitem->{'size'}, $biblioitem->{'place'},
3395 $biblioitem->{'lccn'}, $biblioitem->{'marc'}, $biblioitem->{'url'}, $biblioitem->{'biblioitems.cn_source'},
3396 $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'}, $biblioitem->{'cn_suffix'}, $cn_sort,
3397 $biblioitem->{'totalissues'}, $biblioitem->{'ean'}
3399 my $bibitemnum = $dbh->{'mysql_insertid'};
3401 if ( $dbh->errstr ) {
3402 $error .= "ERROR in _koha_add_biblioitem $query" . $dbh->errstr;
3403 warn $error;
3405 $sth->finish();
3406 return ( $bibitemnum, $error );
3409 =head2 _koha_delete_biblio
3411 $error = _koha_delete_biblio($dbh,$biblionumber);
3413 Internal sub for deleting from biblio table -- also saves to deletedbiblio
3415 C<$dbh> - the database handle
3417 C<$biblionumber> - the biblionumber of the biblio to be deleted
3419 =cut
3421 # FIXME: add error handling
3423 sub _koha_delete_biblio {
3424 my ( $dbh, $biblionumber ) = @_;
3426 # get all the data for this biblio
3427 my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
3428 $sth->execute($biblionumber);
3430 if ( my $data = $sth->fetchrow_hashref ) {
3432 # save the record in deletedbiblio
3433 # find the fields to save
3434 my $query = "INSERT INTO deletedbiblio SET ";
3435 my @bind = ();
3436 foreach my $temp ( keys %$data ) {
3437 $query .= "$temp = ?,";
3438 push( @bind, $data->{$temp} );
3441 # replace the last , by ",?)"
3442 $query =~ s/\,$//;
3443 my $bkup_sth = $dbh->prepare($query);
3444 $bkup_sth->execute(@bind);
3445 $bkup_sth->finish;
3447 # delete the biblio
3448 my $sth2 = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
3449 $sth2->execute($biblionumber);
3450 # update the timestamp (Bugzilla 7146)
3451 $sth2= $dbh->prepare("UPDATE deletedbiblio SET timestamp=NOW() WHERE biblionumber=?");
3452 $sth2->execute($biblionumber);
3453 $sth2->finish;
3455 $sth->finish;
3456 return undef;
3459 =head2 _koha_delete_biblioitems
3461 $error = _koha_delete_biblioitems($dbh,$biblioitemnumber);
3463 Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems
3465 C<$dbh> - the database handle
3466 C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted
3468 =cut
3470 # FIXME: add error handling
3472 sub _koha_delete_biblioitems {
3473 my ( $dbh, $biblioitemnumber ) = @_;
3475 # get all the data for this biblioitem
3476 my $sth = $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
3477 $sth->execute($biblioitemnumber);
3479 if ( my $data = $sth->fetchrow_hashref ) {
3481 # save the record in deletedbiblioitems
3482 # find the fields to save
3483 my $query = "INSERT INTO deletedbiblioitems SET ";
3484 my @bind = ();
3485 foreach my $temp ( keys %$data ) {
3486 $query .= "$temp = ?,";
3487 push( @bind, $data->{$temp} );
3490 # replace the last , by ",?)"
3491 $query =~ s/\,$//;
3492 my $bkup_sth = $dbh->prepare($query);
3493 $bkup_sth->execute(@bind);
3494 $bkup_sth->finish;
3496 # delete the biblioitem
3497 my $sth2 = $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
3498 $sth2->execute($biblioitemnumber);
3499 # update the timestamp (Bugzilla 7146)
3500 $sth2= $dbh->prepare("UPDATE deletedbiblioitems SET timestamp=NOW() WHERE biblioitemnumber=?");
3501 $sth2->execute($biblioitemnumber);
3502 $sth2->finish;
3504 $sth->finish;
3505 return undef;
3508 =head1 UNEXPORTED FUNCTIONS
3510 =head2 ModBiblioMarc
3512 &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
3514 Add MARC data for a biblio to koha
3516 Function exported, but should NOT be used, unless you really know what you're doing
3518 =cut
3520 sub ModBiblioMarc {
3521 # pass the MARC::Record to this function, and it will create the records in
3522 # the marc field
3523 my ( $record, $biblionumber, $frameworkcode ) = @_;
3525 # Clone record as it gets modified
3526 $record = $record->clone();
3527 my $dbh = C4::Context->dbh;
3528 my @fields = $record->fields();
3529 if ( !$frameworkcode ) {
3530 $frameworkcode = "";
3532 my $sth = $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
3533 $sth->execute( $frameworkcode, $biblionumber );
3534 $sth->finish;
3535 my $encoding = C4::Context->preference("marcflavour");
3537 # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
3538 if ( $encoding eq "UNIMARC" ) {
3539 my $string = $record->subfield( 100, "a" );
3540 if ( ($string) && ( length( $record->subfield( 100, "a" ) ) == 36 ) ) {
3541 my $f100 = $record->field(100);
3542 $record->delete_field($f100);
3543 } else {
3544 $string = POSIX::strftime( "%Y%m%d", localtime );
3545 $string =~ s/\-//g;
3546 $string = sprintf( "%-*s", 35, $string );
3548 substr( $string, 22, 6, "frey50" );
3549 unless ( $record->subfield( 100, "a" ) ) {
3550 $record->insert_fields_ordered( MARC::Field->new( 100, "", "", "a" => $string ) );
3554 #enhancement 5374: update transaction date (005) for marc21/unimarc
3555 if($encoding =~ /MARC21|UNIMARC/) {
3556 my @a= (localtime) [5,4,3,2,1,0]; $a[0]+=1900; $a[1]++;
3557 # YY MM DD HH MM SS (update year and month)
3558 my $f005= $record->field('005');
3559 $f005->update(sprintf("%4d%02d%02d%02d%02d%04.1f",@a)) if $f005;
3562 my $oldRecord;
3563 if ( C4::Context->preference("NoZebra") ) {
3565 # only NoZebra indexing needs to have
3566 # the previous version of the record
3567 $oldRecord = GetMarcBiblio($biblionumber);
3569 $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
3570 $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding), $biblionumber );
3571 $sth->finish;
3572 ModZebra( $biblionumber, "specialUpdate", "biblioserver", $oldRecord, $record );
3573 return $biblionumber;
3576 =head2 get_biblio_authorised_values
3578 find the types and values for all authorised values assigned to this biblio.
3580 parameters:
3581 biblionumber
3582 MARC::Record of the bib
3584 returns: a hashref mapping the authorised value to the value set for this biblionumber
3586 $authorised_values = {
3587 'Scent' => 'flowery',
3588 'Audience' => 'Young Adult',
3589 'itemtypes' => 'SER',
3592 Notes: forlibrarian should probably be passed in, and called something different.
3594 =cut
3596 sub get_biblio_authorised_values {
3597 my $biblionumber = shift;
3598 my $record = shift;
3600 my $forlibrarian = 1; # are we in staff or opac?
3601 my $frameworkcode = GetFrameworkCode($biblionumber);
3603 my $authorised_values;
3605 my $tagslib = GetMarcStructure( $forlibrarian, $frameworkcode )
3606 or return $authorised_values;
3608 # assume that these entries in the authorised_value table are bibliolevel.
3609 # ones that start with 'item%' are item level.
3610 my $query = q(SELECT distinct authorised_value, kohafield
3611 FROM marc_subfield_structure
3612 WHERE authorised_value !=''
3613 AND (kohafield like 'biblio%'
3614 OR kohafield like '') );
3615 my $bibliolevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
3617 foreach my $tag ( keys(%$tagslib) ) {
3618 foreach my $subfield ( keys( %{ $tagslib->{$tag} } ) ) {
3620 # warn "checking $subfield. type is: " . ref $tagslib->{ $tag }{ $subfield };
3621 if ( 'HASH' eq ref $tagslib->{$tag}{$subfield} ) {
3622 if ( defined $tagslib->{$tag}{$subfield}{'authorised_value'} && exists $bibliolevel_authorised_values->{ $tagslib->{$tag}{$subfield}{'authorised_value'} } ) {
3623 if ( defined $record->field($tag) ) {
3624 my $this_subfield_value = $record->field($tag)->subfield($subfield);
3625 if ( defined $this_subfield_value ) {
3626 $authorised_values->{ $tagslib->{$tag}{$subfield}{'authorised_value'} } = $this_subfield_value;
3634 # warn ( Data::Dumper->Dump( [ $authorised_values ], [ 'authorised_values' ] ) );
3635 return $authorised_values;
3638 =head2 CountBiblioInOrders
3640 =over 4
3641 $count = &CountBiblioInOrders( $biblionumber);
3643 =back
3645 This function return count of biblios in orders with $biblionumber
3647 =cut
3649 sub CountBiblioInOrders {
3650 my ($biblionumber) = @_;
3651 my $dbh = C4::Context->dbh;
3652 my $query = "SELECT count(*)
3653 FROM aqorders
3654 WHERE biblionumber=? AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')";
3655 my $sth = $dbh->prepare($query);
3656 $sth->execute($biblionumber);
3657 my $count = $sth->fetchrow;
3658 return ($count);
3661 =head2 GetSubscriptionsId
3663 =over 4
3664 $subscriptions = &GetSubscriptionsId($biblionumber);
3666 =back
3668 This function return an array of subscriptionid with $biblionumber
3670 =cut
3672 sub GetSubscriptionsId {
3673 my ($biblionumber) = @_;
3674 my $dbh = C4::Context->dbh;
3675 my $query = "SELECT subscriptionid
3676 FROM subscription
3677 WHERE biblionumber=?";
3678 my $sth = $dbh->prepare($query);
3679 $sth->execute($biblionumber);
3680 my @subscriptions = $sth->fetchrow_array;
3681 return (@subscriptions);
3684 =head2 GetHolds
3686 =over 4
3687 $holds = &GetHolds($biblionumber);
3689 =back
3691 This function return the count of holds with $biblionumber
3693 =cut
3695 sub GetHolds {
3696 my ($biblionumber) = @_;
3697 my $dbh = C4::Context->dbh;
3698 my $query = "SELECT count(*)
3699 FROM reserves
3700 WHERE biblionumber=?";
3701 my $sth = $dbh->prepare($query);
3702 $sth->execute($biblionumber);
3703 my $holds = $sth->fetchrow;
3704 return ($holds);
3707 =head2 prepare_host_field
3709 $marcfield = prepare_host_field( $hostbiblioitem, $marcflavour );
3710 Generate the host item entry for an analytic child entry
3712 =cut
3714 sub prepare_host_field {
3715 my ( $hostbiblio, $marcflavour ) = @_;
3716 $marcflavour ||= C4::Context->preference('marcflavour');
3717 my $host = GetMarcBiblio($hostbiblio);
3718 # unfortunately as_string does not 'do the right thing'
3719 # if field returns undef
3720 my %sfd;
3721 my $field;
3722 my $host_field;
3723 if ( $marcflavour eq 'MARC21' || $marcflavour eq 'NORMARC' ) {
3724 if ( $field = $host->field('100') || $host->field('110') || $host->field('11') ) {
3725 my $s = $field->as_string('ab');
3726 if ($s) {
3727 $sfd{a} = $s;
3730 if ( $field = $host->field('245') ) {
3731 my $s = $field->as_string('a');
3732 if ($s) {
3733 $sfd{t} = $s;
3736 if ( $field = $host->field('260') ) {
3737 my $s = $field->as_string('abc');
3738 if ($s) {
3739 $sfd{d} = $s;
3742 if ( $field = $host->field('240') ) {
3743 my $s = $field->as_string();
3744 if ($s) {
3745 $sfd{b} = $s;
3748 if ( $field = $host->field('022') ) {
3749 my $s = $field->as_string('a');
3750 if ($s) {
3751 $sfd{x} = $s;
3754 if ( $field = $host->field('020') ) {
3755 my $s = $field->as_string('a');
3756 if ($s) {
3757 $sfd{z} = $s;
3760 if ( $field = $host->field('001') ) {
3761 $sfd{w} = $field->data(),;
3763 $host_field = MARC::Field->new( 773, '0', ' ', %sfd );
3764 return $host_field;
3766 elsif ( $marcflavour eq 'UNIMARC' ) {
3767 #author
3768 if ( $field = $host->field('700') || $host->field('710') || $host->field('720') ) {
3769 my $s = $field->as_string('ab');
3770 if ($s) {
3771 $sfd{a} = $s;
3774 #title
3775 if ( $field = $host->field('200') ) {
3776 my $s = $field->as_string('a');
3777 if ($s) {
3778 $sfd{t} = $s;
3781 #place of publicaton
3782 if ( $field = $host->field('210') ) {
3783 my $s = $field->as_string('a');
3784 if ($s) {
3785 $sfd{c} = $s;
3788 #date of publication
3789 if ( $field = $host->field('210') ) {
3790 my $s = $field->as_string('d');
3791 if ($s) {
3792 $sfd{d} = $s;
3795 #edition statement
3796 if ( $field = $host->field('205') ) {
3797 my $s = $field->as_string();
3798 if ($s) {
3799 $sfd{a} = $s;
3802 #URL
3803 if ( $field = $host->field('856') ) {
3804 my $s = $field->as_string('u');
3805 if ($s) {
3806 $sfd{u} = $s;
3809 #ISSN
3810 if ( $field = $host->field('011') ) {
3811 my $s = $field->as_string('a');
3812 if ($s) {
3813 $sfd{x} = $s;
3816 #ISBN
3817 if ( $field = $host->field('010') ) {
3818 my $s = $field->as_string('a');
3819 if ($s) {
3820 $sfd{y} = $s;
3823 if ( $field = $host->field('001') ) {
3824 $sfd{0} = $field->data(),;
3826 $host_field = MARC::Field->new( 461, '0', ' ', %sfd );
3827 return $host_field;
3829 return;
3833 =head2 UpdateTotalIssues
3835 UpdateTotalIssues($biblionumber, $increase, [$value])
3837 Update the total issue count for a particular bib record.
3839 =over 4
3841 =item C<$biblionumber> is the biblionumber of the bib to update
3843 =item C<$increase> is the amount to increase (or decrease) the total issues count by
3845 =item C<$value> is the absolute value that total issues count should be set to. If provided, C<$increase> is ignored.
3847 =back
3849 =cut
3851 sub UpdateTotalIssues {
3852 my ($biblionumber, $increase, $value) = @_;
3853 my $totalissues;
3855 my $data = GetBiblioData($biblionumber);
3857 if (defined $value) {
3858 $totalissues = $value;
3859 } else {
3860 $totalissues = $data->{'totalissues'} + $increase;
3862 my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField('biblioitems.totalissues', $data->{'frameworkcode'});
3864 my $record = GetMarcBiblio($biblionumber);
3866 my $field = $record->field($totalissuestag);
3867 if (defined $field) {
3868 $field->update( $totalissuessubfield => $totalissues );
3869 } else {
3870 $field = MARC::Field->new($totalissuestag, '0', '0',
3871 $totalissuessubfield => $totalissues);
3872 $record->insert_grouped_field($field);
3875 ModBiblio($record, $biblionumber, $data->{'frameworkcode'});
3876 return;
3882 __END__
3884 =head1 AUTHOR
3886 Koha Development Team <http://koha-community.org/>
3888 Paul POULAIN paul.poulain@free.fr
3890 Joshua Ferraro jmf@liblime.com
3892 =cut