From 04bcc56a1c522874bbddba678cc6069cf67eddf2 Mon Sep 17 00:00:00 2001 From: Hector Castro <hector.hecaxmmx@gmail.com> Date: Sun, 8 Nov 2015 23:22:47 -0600 Subject: [PATCH] Bug 13642 [ENH] Adding new features for Dublin Core metadata When Koha export a bibliographic record to DC, makes it in XML format. This XML not follows the DC-XML recommendations as should be: elements in uppercase eg. dc:Date, dc:Creator, section 4.2, recommendation 4 explain that "The property names for the 15 DC elements should be all lower-case." eg. dc:date, dc:creator" and section "4.3 Example - a simple DC record", xsi:schemaLocation="http://example.org/myapp/ http://example.org/myapp/schema.xsd" schema does not exist. NOTE: This new feature implement the XSLT transformation for OAI-DC, SRW-DC and RDF-DC Test plan --------------- 1) Download Dublin Core file from record detail page. Open up the file, and make sure that the document not follows the DC-XML recommendations as should be. 2) Apply patch. 3) Go to whichever bib record in OPAC or staff and click on Save > Dublin Core. A modal will display, prove all options. 4) Change the system preference 'Opac ExportOptions' by enabling and disabling Dublin Core and try to download a record. 5) Try several bibliographic records in any format (book, magazine, DVD, etc.) to confirm that properly exported. 6) Test with all marc flavours. Sponsored-by: Universidad de El Salvador --- C4/Record.pm | 113 ++++++++++++++------ catalogue/export.pl | 4 +- .../intranet-tmpl/prog/en/includes/cat-toolbar.inc | 2 +- .../prog/en/modules/catalogue/detail.tt | 32 +++++- .../bootstrap/en/includes/opac-detail-sidebar.inc | 5 +- .../opac-tmpl/bootstrap/en/modules/opac-detail.tt | 29 +++++ koha-tmpl/opac-tmpl/bootstrap/less/opac.less | 7 ++ opac/opac-export.pl | 4 +- 8 files changed, 154 insertions(+), 42 deletions(-) diff --git a/C4/Record.pm b/C4/Record.pm index f46651b..746dd2d 100644 --- a/C4/Record.pm +++ b/C4/Record.pm @@ -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. # @@ -25,7 +26,6 @@ use strict; # 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 @@ -36,6 +36,8 @@ use YAML; #marcrecords2csv use Template; use Text::CSV::Encoded; #marc2csv use Koha::SimpleMARC qw(read_field); +use Koha::XSLT_Handler; +use Carp; use vars qw($VERSION @ISA @EXPORT); @@ -220,51 +222,92 @@ 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 database (biblioitems.marcxml) +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> - obtain the record directly from database (biblioitems.marcxml) + +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 from biblioitems.marcxml 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_Handler->new; + if ( $format eq 'oaidc' || 'srwdc' || 'rdfdc' ) { + $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 number + my $errstr = $xslt_engine->errstr; # error message + if ( $err ) { + croak "Error when processing $errstr Error number: $err\n"; + } else { + return $output; + } } - $dcxmlfinal .= "\n</metadata>"; - return ($error,$dcxmlfinal); } =head2 marc2modsxml - Convert from ISO-2709 to MODS diff --git a/catalogue/export.pl b/catalogue/export.pl index e66d3f2..39cac7c 100755 --- a/catalogue/export.pl +++ b/catalogue/export.pl @@ -50,8 +50,8 @@ if ($op eq "export") { $marc = marc2bibtex($marc); $format = "bibtex"; } - elsif ($format =~ /dc/) { - ($error,$marc) = marc2dcxml($marc,1); + elsif ($format =~ /dc$/) { + $marc = marc2dcxml(undef, undef, $biblionumber, $format); $format = "dublin-core.xml"; } elsif ($format =~ /marc8/) { diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc index 3e604d2..bd43416 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc @@ -217,7 +217,7 @@ CAN_user_serials_create_subscription ) %] <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"><i class="fa fa-download"></i> Save <span class="caret"></span></button> <ul class="dropdown-menu"> <li><a href="/cgi-bin/koha/catalogue/export.pl?format=bibtex&op=export&bib=[% biblionumber %]">BIBTEX</a></li> - <li><a href="/cgi-bin/koha/catalogue/export.pl?format=dc&op=export&bib=[% biblionumber %]">Dublin Core (XML)</a></li> + <li><a href="#" data-toggle="modal" data-target="#exportModal_">Dublin Core</a></li> <li><a href="/cgi-bin/koha/catalogue/export.pl?format=marcxml&op=export&bib=[% biblionumber %]">MARCXML</a></li> <li><a href="/cgi-bin/koha/catalogue/export.pl?format=marc8&op=export&bib=[% biblionumber %]">MARC (non-Unicode/MARC-8)</a></li> <li><a href="/cgi-bin/koha/catalogue/export.pl?format=utf8&op=export&bib=[% biblionumber %]">MARC (Unicode/UTF-8)</a></li> diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt index a83b6ba..7f51132 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt @@ -1050,7 +1050,7 @@ function verify_images() { <th>Save Record</th> </tr> <tr><td> Select download format: <select name="format"> <option value="mods">MODS (XML)</option> - <option value="dc">Dublin Core (XML)</option> + <option data-toggle="modal" data-target="#exportModal_">Dublin Core</option> <option value="marcxml">MARCXML</option> <option value="marc8">MARC (non-Unicode/MARC-8)</option> <option value="utf8">MARC (Unicode/UTF-8)</option> </select> @@ -1083,4 +1083,34 @@ function verify_images() { </div> [% END %] </div> + + <!--Modal for Dublin Core--> + <div class="modal hide" id="exportModal_" tabindex="-1" role="dialog" aria-labelledby="exportLabelexportModal_" aria-hidden="true"> + <div class="modal-header"> + <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button> + <h3 id="exportLabelexportModal_">Exporting to Dublin Core...</h3> + </div> + <form method="get" action="/cgi-bin/koha/catalogue/export.pl"> + <div class="modal-body"> + <fieldset> + <input id="input-simple" type="radio" name="format" value="rdfdc"> + <label for="input-simple">Simple DC-RDF</label> + <br> + <input id="input-oai" type="radio" name="format" value="oaidc"> + <label for="input-oai">OAI-DC</label> + <br> + <input id="input-srw" type="radio" name="format" value="srwdc"> + <label for="input-srw">SRW-DC</label> + <br> + </fieldset> + </div> + <div class="modal-footer"> + <button type="submit" class="btn">Export</button> + <button class="btn btn-link" data-dismiss="modal" aria-hidden="true">Cancel</button> + </div> + <input type="hidden" name="op" value="export" /> + <input type="hidden" name="bib" value="[% biblionumber %]" /> + </form> + </div> + [% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc index a4ef40e..b9972e7 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc @@ -38,11 +38,13 @@ <a id="format" class="dropdown-toggle" data-toggle="dropdown" href="#">Save record <b class="caret"></b></a> <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="format"> [% FOREACH option IN export_options %] + [% IF option == 'dc' %] + <li><a role="menuitem" href="#" data-toggle="modal" data-target="#exportModal_">Dublin Core</a></li> + [% ELSE %] <li> <a role="menuitem" href="/cgi-bin/koha/opac-export.pl?op=export&bib=[% biblionumber %]&format=[% option %]"> [% SWITCH option %] [% CASE 'bibtex' %]BIBTEX - [% CASE 'dc' %]Dublin Core (XML) [% CASE 'endnote' %]EndNote [% CASE 'marcxml' %]MARCXML [% CASE 'marc8' %]MARC (non-Unicode/MARC-8) @@ -54,6 +56,7 @@ [% END %] </a> </li> + [% END %] [% END %] </ul> </div> diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt index 410f795..d1ea0c6 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt @@ -1154,6 +1154,35 @@ </div> <!-- / .container-fluid --> </div> <!-- / .main --> +<!-- Dublin Core Modal Form --> +<div class="modal hide" id="exportModal_" tabindex="-1" role="dialog" aria-labelledby="exportLabelexportModal_" aria-hidden="true"> + <div class="modal-header"> + <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button> + <h3 class="modal-title" id="exportModalLabel">Exporting to Dublin Core...</h3> + </div> + <form method="get" action="/cgi-bin/koha/opac-export.pl"> + <div class="modal-body"> + <fieldset class="dc_fieldset"> + <input id="input-simple" type="radio" name="format" value="rdfdc"> + <label for="input-simple">Simple DC-RDF</label> + <br> + <input id="input-oai" type="radio" name="format" value="oaidc"> + <label for="input-oai">OAI-DC</label> + <br> + <input id="input-srw" type="radio" name="format" value="srwdc"> + <label for="input-srw">SRW-DC</label> + <br> + <input type="hidden" name="op" value="export"> + <input type="hidden" name="bib" value="[% biblionumber %]"> + </fieldset> + </div> + <div class="modal-footer"> + <button type="submit" class="btn">Export</button> + <button class="btn btn-link" data-dismiss="modal" aria-hidden="true">Cancel</button> + </div> + </form> +</div> + [% INCLUDE 'opac-bottom.inc' %] [%# End of template %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less index 6da32e5..56bffa4 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less +++ b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less @@ -2490,4 +2490,11 @@ a.reviewlink:visited { font-size: 90%; } +#dc_fieldset { + border:1px solid #dddddd; + border-width:1px; + padding:5px; + border-radius:10px +} + @import "responsive.less"; diff --git a/opac/opac-export.pl b/opac/opac-export.pl index bf040db..c4ab83a 100755 --- a/opac/opac-export.pl +++ b/opac/opac-export.pl @@ -60,8 +60,8 @@ elsif ($format =~ /bibtex/) { $marc = marc2bibtex(C4::Biblio::GetMarcBiblio($biblionumber),$biblionumber); $format = 'bibtex'; } -elsif ($format =~ /dc/) { - ($error,$marc) = marc2dcxml($marc,1); +elsif ($format =~ /dc$/) { + $marc = marc2dcxml(undef, undef, $biblionumber, $format); $format = "dublin-core.xml"; } elsif ($format =~ /marc8/) { -- 1.7.10.4