Bug 17600: Standardize our EXPORT_OK
[srvgit] / C4 / Record.pm
index a3bc860..d2377ed 100644 (file)
@@ -2,6 +2,7 @@ package C4::Record;
 #
 # Copyright 2006 (C) LibLime
 # Parts copyright 2010 BibLibre
+# Part copyright 2015 Universidad de El Salvador
 #
 # This file is part of Koha.
 #
@@ -17,46 +18,47 @@ package C4::Record;
 #
 # You should have received a copy of the GNU General Public License
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
-#
-#
-use strict;
-#use warnings; FIXME - Bug 2505
+
+use Modern::Perl;
 
 # please specify in which methods a given module is used
 use MARC::Record; # marc2marcxml, marcxml2marc, changeEncoding
 use MARC::File::XML; # marc2marcxml, marcxml2marc, changeEncoding
-use MARC::Crosswalk::DublinCore; # marc2dcxml
 use Biblio::EndnoteStyle;
-use Unicode::Normalize; # _entity_encode
-use C4::Biblio; #marc2bibtex
-use C4::Csv; #marc2csv
+use Unicode::Normalize qw( NFC ); # _entity_encode
+use C4::Biblio qw( GetFrameworkCode GetMarcBiblio );
 use C4::Koha; #marc2csv
-use C4::XSLT ();
-use YAML; #marcrecords2csv
+use C4::XSLT;
+use YAML::XS; #marcrecords2csv
+use Encode;
 use Template;
 use Text::CSV::Encoded; #marc2csv
-use Koha::SimpleMARC qw(read_field);
+use Koha::Items;
+use Koha::SimpleMARC qw( read_field );
+use Koha::XSLT::Base;
+use Koha::CsvProfiles;
+use Koha::AuthorisedValues;
+use Carp qw( carp croak );
 
-use vars qw($VERSION @ISA @EXPORT);
+use vars qw(@ISA @EXPORT);
 
-# set the version for version checking
-$VERSION = 3.07.00.049;
 
 @ISA = qw(Exporter);
 
 # only export API methods
 
 @EXPORT = qw(
-  &marc2endnote
-  &marc2marc
-  &marc2marcxml
-  &marcxml2marc
-  &marc2dcxml
-  &marc2modsxml
-  &marc2madsxml
-  &marc2bibtex
-  &marc2csv
-  &changeEncoding
+  marc2endnote
+  marc2marc
+  marc2marcxml
+  marcxml2marc
+  marc2dcxml
+  marc2modsxml
+  marc2madsxml
+  marc2bibtex
+  marc2csv
+  marcrecord2csv
+  changeEncoding
 );
 
 =head1 NAME
@@ -80,7 +82,7 @@ Returns an ISO-2709 scalar
 sub marc2marc {
        my ($marc,$to_flavour,$from_flavour,$encoding) = @_;
        my $error;
-    if ($to_flavour =~ m/marcstd/) {
+    if ($to_flavour && $to_flavour =~ m/marcstd/) {
         my $marc_record_obj;
         if ($marc =~ /^MARC::Record/) { # it's already a MARC::Record object
             $marc_record_obj = $marc;
@@ -220,51 +222,91 @@ sub marcxml2marc {
 
 =head2 marc2dcxml - Convert from ISO-2709 to Dublin Core
 
-  my ($error,$dcxml) = marc2dcxml($marc,$qualified);
+    my dcxml = marc2dcxml ($marc, $xml, $biblionumber, $format);
 
-Returns a DublinCore::Record object, will eventually return a Dublin Core scalar
+EXAMPLE
 
-FIXME: should return actual XML, not just an object
+    my dcxml = marc2dcxml (undef, undef, 1, "oaidc");
+
+Convert MARC or MARCXML to Dublin Core metadata (XSLT Transformation),
+optionally can get an XML directly from biblio_metadata
+without item information. This method take into consideration the syspref
+'marcflavour' (UNIMARC, MARC21 and NORMARC).
+Return an XML file with the format defined in C<$format>
 
 C<$marc> - an ISO-2709 scalar or MARC::Record object
 
-C<$qualified> - specify whether qualified Dublin Core should be used in the input or output [0]
+C<$xml> - a MARCXML file
+
+C<$biblionumber> - biblionumber for database access
+
+C<$format> - accept three type of DC formats (oaidc, srwdc, and rdfdc )
 
 =cut
 
 sub marc2dcxml {
-       my ($marc,$qualified) = @_;
-       my $error;
-    # test if it's already a MARC::Record object, if not, make it one
-    my $marc_record_obj;
-    if ($marc =~ /^MARC::Record/) { # it's already a MARC::Record object
-        $marc_record_obj = $marc;
-    } else { # it's not a MARC::Record object, make it one
-               eval { $marc_record_obj = MARC::Record->new_from_usmarc($marc) }; # handle exceptions
+    my ( $marc, $xml, $biblionumber, $format ) = @_;
+
+    # global variables
+    my ( $marcxml, $record, $output );
+
+    # set the default path for intranet xslts
+    # differents xslts to process (OAIDC, SRWDC and RDFDC)
+    my $xsl = C4::Context->config('intrahtdocs') . '/prog/en/xslt/' .
+              C4::Context->preference('marcflavour') . 'slim2' . uc ( $format ) . '.xsl';
+
+    if ( defined $marc ) {
+        # no need to catch errors or warnings marc2marcxml do it instead
+        $marcxml = C4::Record::marc2marcxml( $marc );
+    } elsif ( not defined $xml and defined $biblionumber ) {
+        # get MARCXML biblio directly without item information
+        $marcxml = C4::Biblio::GetXmlBiblio( $biblionumber );
+    } else {
+        $marcxml = $xml;
+    }
 
-               # conversion to MARC::Record object failed, populate $error
-               if ($@) {
-                       $error .="\nCreation of MARC::Record object failed: ".$MARC::File::ERROR;
-               }
-       }
-       my $crosswalk = MARC::Crosswalk::DublinCore->new;
-       if ($qualified) {
-               $crosswalk = MARC::Crosswalk::DublinCore->new( qualified => 1 );
-       }
-       my $dcxml = $crosswalk->as_dublincore($marc_record_obj);
-       my $dcxmlfinal = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
-       $dcxmlfinal .= "<metadata
-  xmlns=\"http://example.org/myapp/\"
-  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
-  xsi:schemaLocation=\"http://example.org/myapp/ http://example.org/myapp/schema.xsd\"
-  xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
-  xmlns:dcterms=\"http://purl.org/dc/terms/\">";
-
-       foreach my $element ( $dcxml->elements() ) {
-                $dcxmlfinal.="<"."dc:".$element->name().">".$element->content()."</"."dc:".$element->name().">\n";
+    # only proceed if MARC21 or UNIMARC; else clause is executed if marcflavour set it to NORMARC
+    # generate MARC::Record object to see if not a marcxml record
+    unless ( C4::Context->preference('marcflavour') eq 'NORMARC' ) {
+        eval { $record = MARC::Record->new_from_xml(
+                         $marcxml,
+                         'UTF-8',
+                         C4::Context->preference('marcflavour')
+               );
+        };
+    } else {
+        eval { $record = MARC::Record->new_from_xml(
+                         $marcxml,
+                        'UTF-8',
+                        'MARC21'
+               );
+        };
+    }
+
+    # conversion to MARC::Record object failed
+    if ( $@ ) {
+        croak "Creation of MARC::Record object failed.";
+    } elsif ( $record->warnings() ) {
+        carp "Warnings encountered while processing ISO-2709 record.\n";
+        my @warnings = $record->warnings();
+        foreach my $warn (@warnings) {
+            carp "\t". $warn;
+        };
+    } elsif ( $record =~ /^MARC::Record/ ) { # if OK makes xslt transformation
+        my $xslt_engine = Koha::XSLT::Base->new;
+        if ( $format =~ /^(dc|oaidc|srwdc|rdfdc)$/i ) {
+            $output = $xslt_engine->transform( $marcxml, $xsl );
+        } else {
+            croak "The format argument ($format) not accepted.\n" .
+                  "Please pass a valid format (oaidc, srwdc, or rdfdc)\n";
+        }
+        my $err = $xslt_engine->err; # error code
+        if ( $err ) {
+            croak "Error $err while processing\n";
+        } else {
+            return $output;
+        }
     }
-       $dcxmlfinal .= "\n</metadata>";
-       return ($error,$dcxmlfinal);
 }
 
 =head2 marc2modsxml - Convert from ISO-2709 to MODS
@@ -326,27 +368,26 @@ sub marc2endnote {
     if ($f500) {
         $abstract = $f500->subfield('a');
     }
-       my $fields = {
-               DB => C4::Context->preference("LibraryName"),
-               Title => $marc_rec_obj->title(),
-               Author => $marc_rec_obj->author(),
-               Publisher => $f710a,
-               City => $f260a,
-               Year => $marc_rec_obj->publication_date,
-               Abstract => $abstract,
-       };
-       my $endnote;
-       my $style = new Biblio::EndnoteStyle();
-       my $template;
-       $template.= "DB - DB\n" if C4::Context->preference("LibraryName");
-       $template.="T1 - Title\n" if $marc_rec_obj->title();
-       $template.="A1 - Author\n" if $marc_rec_obj->author();
-       $template.="PB - Publisher\n" if  $f710a;
-       $template.="CY - City\n" if $f260a;
-       $template.="Y1 - Year\n" if $marc_rec_obj->publication_date;
-       $template.="AB - Abstract\n" if $abstract;
-       my ($text, $errmsg) = $style->format($template, $fields);
-       return ($text);
+    my $fields = {
+        DB => C4::Context->preference("LibraryName"),
+        Title => $marc_rec_obj->title(),
+        Author => $marc_rec_obj->author(),
+        Publisher => $f710a,
+        City => $f260a,
+        Year => $marc_rec_obj->publication_date,
+        Abstract => $abstract,
+    };
+    my $style = Biblio::EndnoteStyle->new();
+    my $template;
+    $template.= "DB - DB\n" if C4::Context->preference("LibraryName");
+    $template.="T1 - Title\n" if $marc_rec_obj->title();
+    $template.="A1 - Author\n" if $marc_rec_obj->author();
+    $template.="PB - Publisher\n" if  $f710a;
+    $template.="CY - City\n" if $f260a;
+    $template.="Y1 - Year\n" if $marc_rec_obj->publication_date;
+    $template.="AB - Abstract\n" if $abstract;
+    my ($text, $errmsg) = $style->format($template, $fields);
+    return ($text);
 
 }
 
@@ -360,7 +401,7 @@ Returns a CSV scalar
 
 C<$biblio> - a list of biblionumbers
 
-C<$csvprofileid> - the id of the CSV profile to use for the export (see export_format.export_format_id and the GetCsvProfiles function in C4::Csv)
+C<$csvprofileid> - the id of the CSV profile to use for the export (see export_format.export_format_id)
 
 C<$itemnumbers> - a list of itemnumbers to export
 
@@ -376,28 +417,29 @@ sub marc2csv {
     my $configfile = "../tools/csv-profiles/$id.yaml";
     my ($preprocess, $postprocess, $fieldprocessing);
     if (-e $configfile){
-        ($preprocess,$postprocess, $fieldprocessing) = YAML::LoadFile($configfile);
+        ($preprocess,$postprocess, $fieldprocessing) = YAML::XS::LoadFile($configfile);
     }
 
     # Preprocessing
-    eval $preprocess if ($preprocess);
+    eval $preprocess if ($preprocess); ## no critic (StringyEval)
 
     my $firstpass = 1;
     if ( @$itemnumbers ) {
         for my $itemnumber ( @$itemnumbers) {
-            my $biblionumber = GetBiblionumberFromItemnumber $itemnumber;
-            $output .= marcrecord2csv( $biblionumber, $id, $firstpass, $csv, $fieldprocessing, [$itemnumber] );
+            my $item = Koha::Items->find( $itemnumber );
+            my $biblionumber = $item->biblio->biblionumber;
+            $output .= marcrecord2csv( $biblionumber, $id, $firstpass, $csv, $fieldprocessing, [$itemnumber] ) // '';
             $firstpass = 0;
         }
     } else {
         foreach my $biblio (@$biblios) {
-            $output .= marcrecord2csv( $biblio, $id, $firstpass, $csv, $fieldprocessing );
+            $output .= marcrecord2csv( $biblio, $id, $firstpass, $csv, $fieldprocessing ) // '';
             $firstpass = 0;
         }
     }
 
     # Postprocessing
-    eval $postprocess if ($postprocess);
+    eval $postprocess if ($postprocess); ## no critic (StringyEval)
 
     return $output;
 }
@@ -410,7 +452,7 @@ Returns a CSV scalar
 
 C<$biblio> - a biblionumber
 
-C<$csvprofileid> - the id of the CSV profile to use for the export (see export_format.export_format_id and the GetCsvProfiles function in C4::Csv)
+C<$csvprofileid> - the id of the CSV profile to use for the export (see export_format.export_format_id)
 
 C<$header> - true if the headers are to be printed (typically at first pass)
 
@@ -427,21 +469,24 @@ sub marcrecord2csv {
     my $output;
 
     # Getting the record
-    my $record = GetMarcBiblio($biblio);
+    my $record = GetMarcBiblio({ biblionumber => $biblio });
     return unless $record;
-    C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblio, $itemnumbers );
+    C4::Biblio::EmbedItemsInMarcBiblio({
+        marc_record  => $record,
+        biblionumber => $biblio,
+        item_numbers => $itemnumbers });
     # Getting the framework
     my $frameworkcode = GetFrameworkCode($biblio);
 
     # Getting information about the csv profile
-    my $profile = GetCsvProfile($id);
+    my $profile = Koha::CsvProfiles->find($id);
 
     # Getting output encoding
-    my $encoding          = $profile->{encoding} || 'utf8';
+    my $encoding          = $profile->encoding || 'utf8';
     # Getting separators
-    my $csvseparator      = $profile->{csv_separator}      || ',';
-    my $fieldseparator    = $profile->{field_separator}    || '#';
-    my $subfieldseparator = $profile->{subfield_separator} || '|';
+    my $csvseparator      = $profile->csv_separator      || ',';
+    my $fieldseparator    = $profile->field_separator    || '#';
+    my $subfieldseparator = $profile->subfield_separator || '|';
 
     # TODO: Be more generic (in case we have to handle other protected chars or more separators)
     if ($csvseparator eq '\t') { $csvseparator = "\t" }
@@ -455,7 +500,7 @@ sub marcrecord2csv {
     $csv->sep_char($csvseparator);
 
     # Getting the marcfields
-    my $marcfieldslist = $profile->{content};
+    my $marcfieldslist = $profile->content;
 
     # Getting the marcfields as an array
     my @marcfieldsarray = split('\|', $marcfieldslist);
@@ -470,7 +515,7 @@ sub marcrecord2csv {
         my @fields;
         while ( $content =~ m|(\d{3})\$?(.)?|g ) {
             my $fieldtag = $1;
-            my $subfieldtag = $2 || undef;
+            my $subfieldtag = $2;
             push @fields, { fieldtag => $fieldtag, subfieldtag => $subfieldtag };
         }
         if ( @result == 2) {
@@ -515,7 +560,7 @@ sub marcrecord2csv {
             } else {
                 # If not, we get the matching tag name from koha
                 my $tag = $tags->[0];
-                if ( $tag->{subfieldtag} ) {
+                if (defined $tag->{subfieldtag} ) {
                     my $query = "SELECT liblibrarian FROM marc_subfield_structure WHERE tagfield=? AND tagsubfield=?";
                     my @results = $dbh->selectrow_array( $query, {}, $tag->{fieldtag}, $tag->{subfieldtag} );
                     push @marcfieldsheaders, $results[0];
@@ -531,7 +576,6 @@ sub marcrecord2csv {
         if ( $content =~ m|\[\%.*\%\]| ) {
             my $tt = Template->new();
             my $template = $content;
-            my $vars;
             # Replace 00X and 0XX with X or XX
             $content =~ s|fields.00(\d)|fields.$1|g;
             $content =~ s|fields.0(\d{2})|fields.$1|g;
@@ -543,19 +587,23 @@ sub marcrecord2csv {
                 my @fields = $record->field( $tag->{fieldtag} );
                 # If it is a subfield
                 my @loop_values;
-                if ( $tag->{subfieldtag} ) {
+                if (defined $tag->{subfieldtag} ) {
+                    my $av = Koha::AuthorisedValues->search_by_marc_field({ frameworkcode => $frameworkcode, tagfield => $tag->{fieldtag}, tagsubfield => $tag->{subfieldtag}, });
+                    $av = $av->count ? $av->unblessed : [];
+                    my $av_description_mapping = { map { ( $_->{authorised_value} => $_->{lib} ) } @$av };
                     # For each field
                     foreach my $field (@fields) {
                         my @subfields = $field->subfield( $tag->{subfieldtag} );
                         foreach my $subfield (@subfields) {
-                            my $authvalues = GetKohaAuthorisedValuesFromField( $tag->{fieldtag}, $tag->{subfieldtag}, $frameworkcode, undef);
-                            push @loop_values, (defined $authvalues->{$subfield}) ? $authvalues->{$subfield} : $subfield;
+                            push @loop_values, (defined $av_description_mapping->{$subfield}) ? $av_description_mapping->{$subfield} : $subfield;
                         }
                     }
 
                 # Or a field
                 } else {
-                    my $authvalues = GetKohaAuthorisedValuesFromField( $tag->{fieldtag}, undef, $frameworkcode, undef);
+                    my $av = Koha::AuthorisedValues->search_by_marc_field({ frameworkcode => $frameworkcode, tagfield => $tag->{fieldtag}, });
+                    $av = $av->count ? $av->unblessed : [];
+                    my $authvalues = { map { ( $_->{authorised_value} => $_->{lib} ) } @$av };
 
                     foreach my $field ( @fields ) {
                         my $value;
@@ -576,7 +624,7 @@ sub marcrecord2csv {
                         # Field processing
                         my $marcfield = $tag->{fieldtag}; # This line fixes a retrocompatibility concern
                                                           # The "processing" could be based on the $marcfield variable.
-                        eval $fieldprocessing if ($fieldprocessing);
+                        eval $fieldprocessing if ($fieldprocessing); ## no critic (StringyEval)
 
                         push @loop_values, $value;
                     }
@@ -754,7 +802,7 @@ sub marc2bibtex {
     my $additional_fields;
     if ($BibtexExportAdditionalFields) {
         $BibtexExportAdditionalFields = "$BibtexExportAdditionalFields\n\n";
-        $additional_fields = eval { YAML::Load($BibtexExportAdditionalFields); };
+        $additional_fields = eval { YAML::XS::Load(Encode::encode_utf8($BibtexExportAdditionalFields)); };
         if ($@) {
             warn "Unable to parse BibtexExportAdditionalFields : $@";
             $additional_fields = undef;