From 93621a7a026479b4f61e13a1f7a799ca2ac46e91 Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Wed, 6 Nov 2013 16:04:42 +0100 Subject: [PATCH] Bug 8995: Show OpenURL links in OPAC search results This patch use GetCOinS sub to provide an OpenURL link in OPAC search results. It uses 4 new system preferences: - OpenURLinOPACResults: enable or disable this feature - OpenURLResolverURL: url of the openURL resolver - OpenURLText: text of the link - OpenURLImageLocation: image of the link Link is displayed as an image if OpenURLImageLocation is defined, and as text otherwise. It works both with and without XSLT enabled. Changes made to GetCOinSBiblio: For 'journal': - Title should be in rft.jtitle instead of rft.title - rft.date, rft.aulast, rft.aufirst, rft.au, rft.pub and rft.pages have no meaning for a subscription, so they are simply removed from URL This patch refactors GetCOinSBiblio, so the construction of URL is done only at the end. This way we do not have ugly $var .= "&$value" in the function body. Also use URI::Escape instead of custom regexps. This development consider the value of syspref OPACURLOpenInNewWindow when building the link. Test plan: 1/ Enable syspref OPACShowOpenURL and put your OpenURL resolver URL in OpenURLResolverURL syspref (if you don't have one, just fill it with some fake URL, you'll have to check if OpenURL links are correct) 2/ If you want, set the other sysprefs OpenURLImageLocation and OpenURLText 3/ Fill syspref OPACOpenURLItemTypes with some (not all) of your item types. 4/ Empty sysprefs OPACXSLTDetailsDisplay and OPACXSLTResultsDisplay 5/ Go to OPAC and launch a search. 6/ Check you have in the results (near the title) the OpenURL link (only for itemtypes that are in OPACOpenURLItemTypes). Toggle OPACURLOpenInNewWindow syspref and check that the behaviour of the link is correct. 7/ Go to the detail page of one of those and check you have the OpenURL link too. (Above tags) Toggle OPACURLOpenInNewWindow syspref and check that the behaviour of the link is correct. 8/ Set sysprefs OPACXSLTDetailsDisplay and OPACXSLTResultsDisplay to "default" and repeat steps 5 to 7 Signed-off-by: Paola Rossi --- C4/Biblio.pm | 129 ++++++++++++--------- C4/XSLT.pm | 24 +++- .../mysql/atomicupdate/8995_OpenURL_sysprefs.sql | 6 + installer/data/mysql/sysprefs.sql | 5 + .../prog/en/modules/admin/preferences/opac.pref | 27 +++++ .../bootstrap/en/xslt/MARC21slim2OPACDetail.xsl | 64 ++++++++++ .../bootstrap/en/xslt/MARC21slim2OPACResults.xsl | 66 ++++++++++- .../bootstrap/en/xslt/UNIMARCslim2OPACDetail.xsl | 65 +++++++++++ .../bootstrap/en/xslt/UNIMARCslim2OPACResults.xsl | 64 ++++++++++ koha-tmpl/opac-tmpl/bootstrap/less/opac.less | 4 + opac/opac-detail.pl | 14 +++ opac/opac-search.pl | 18 ++- 12 files changed, 424 insertions(+), 62 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/8995_OpenURL_sysprefs.sql diff --git a/C4/Biblio.pm b/C4/Biblio.pm index 2798325..645ca51 100644 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -29,6 +29,7 @@ use MARC::File::USMARC; use MARC::File::XML; use POSIX qw(strftime); use Module::Load::Conditional qw(can_load); +use URI::Escape; # GetCOinSBiblio use C4::Koha; use C4::Log; # logaction @@ -1385,45 +1386,44 @@ sub GetCOinSBiblio { carp 'GetCOinSBiblio called with undefined record'; return; } + my $pos7 = substr $record->leader(), 7, 1; my $pos6 = substr $record->leader(), 6, 1; my $mtx; my $genre; my ( $aulast, $aufirst ) = ( '', '' ); - my $oauthors = ''; - my $title = ''; - my $subtitle = ''; + my @authors; + my $title; + my $hosttitle; my $pubyear = ''; my $isbn = ''; my $issn = ''; my $publisher = ''; my $pages = ''; - my $titletype = 'b'; + my $titletype = ''; # For the purposes of generating COinS metadata, LDR/06-07 can be # considered the same for UNIMARC and MARC21 - my $fmts6; - my $fmts7; - %$fmts6 = ( - 'a' => 'book', - 'b' => 'manuscript', - 'c' => 'book', - 'd' => 'manuscript', - 'e' => 'map', - 'f' => 'map', - 'g' => 'film', - 'i' => 'audioRecording', - 'j' => 'audioRecording', - 'k' => 'artwork', - 'l' => 'document', - 'm' => 'computerProgram', - 'o' => 'document', - 'r' => 'document', - ); - %$fmts7 = ( - 'a' => 'journalArticle', - 's' => 'journal', - ); + my $fmts6 = { + 'a' => 'book', + 'b' => 'manuscript', + 'c' => 'book', + 'd' => 'manuscript', + 'e' => 'map', + 'f' => 'map', + 'g' => 'film', + 'i' => 'audioRecording', + 'j' => 'audioRecording', + 'k' => 'artwork', + 'l' => 'document', + 'm' => 'computerProgram', + 'o' => 'document', + 'r' => 'document', + }; + my $fmts7 = { + 'a' => 'journalArticle', + 's' => 'journal', + }; $genre = $fmts6->{$pos6} ? $fmts6->{$pos6} : 'book'; @@ -1434,6 +1434,7 @@ sub GetCOinSBiblio { ##### We must transform mtx to a valable mtx and document type #### if ( $genre eq 'book' ) { $mtx = 'book'; + $titletype = 'b'; } elsif ( $genre eq 'journal' ) { $mtx = 'journal'; $titletype = 'j'; @@ -1445,26 +1446,25 @@ sub GetCOinSBiblio { $mtx = 'dc'; } - $genre = ( $mtx eq 'dc' ) ? "&rft.type=$genre" : "&rft.genre=$genre"; - if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) { # Setting datas $aulast = $record->subfield( '700', 'a' ) || ''; $aufirst = $record->subfield( '700', 'b' ) || ''; - $oauthors = "&rft.au=$aufirst $aulast"; + push @authors, "$aufirst $aulast" if ($aufirst or $aulast); # others authors if ( $record->field('200') ) { for my $au ( $record->field('200')->subfield('g') ) { - $oauthors .= "&rft.au=$au"; + push @authors, $au; } } - $title = - ( $mtx eq 'dc' ) - ? "&rft.title=" . $record->subfield( '200', 'a' ) - : "&rft.title=" . $record->subfield( '200', 'a' ) . "&rft.btitle=" . $record->subfield( '200', 'a' ); - $pubyear = $record->subfield( '210', 'd' ) || ''; + + $title = $record->subfield( '200', 'a' ); + my $subfield_210d = $record->subfield('210', 'd'); + if ($subfield_210d and $subfield_210d =~ /(\d{4})/) { + $pubyear = $1; + } $publisher = $record->subfield( '210', 'c' ) || ''; $isbn = $record->subfield( '010', 'a' ) || ''; $issn = $record->subfield( '011', 'a' ) || ''; @@ -1474,34 +1474,24 @@ sub GetCOinSBiblio { # Setting datas if ( $record->field('100') ) { - $oauthors .= "&rft.au=" . $record->subfield( '100', 'a' ); + push @authors, $record->subfield( '100', 'a' ); } # others authors if ( $record->field('700') ) { for my $au ( $record->field('700')->subfield('a') ) { - $oauthors .= "&rft.au=$au"; + push @authors, $au; } } - $title = "&rft." . $titletype . "title=" . $record->subfield( '245', 'a' ); - $subtitle = $record->subfield( '245', 'b' ) || ''; - $title .= $subtitle; + $title = $record->subfield( '245', 'a' ) . $record->subfield( '245', 'b' ); if ($titletype eq 'a') { $pubyear = $record->field('008') || ''; $pubyear = substr($pubyear->data(), 7, 4) if $pubyear; $isbn = $record->subfield( '773', 'z' ) || ''; $issn = $record->subfield( '773', 'x' ) || ''; - if ($mtx eq 'journal') { - $title .= "&rft.title=" . (($record->subfield( '773', 't' ) || $record->subfield( '773', 'a'))); - } else { - $title .= "&rft.btitle=" . (($record->subfield( '773', 't' ) || $record->subfield( '773', 'a')) || ''); - } - foreach my $rel ($record->subfield( '773', 'g' )) { - if ($pages) { - $pages .= ', '; - } - $pages .= $rel; - } + $hosttitle = $record->subfield( '773', 't' ) || $record->subfield( '773', 'a'); + my @rels = $record->subfield( '773', 'g' ); + $pages = join(', ', @rels); } else { $pubyear = $record->subfield( '260', 'c' ) || ''; $publisher = $record->subfield( '260', 'b' ) || ''; @@ -1510,12 +1500,39 @@ sub GetCOinSBiblio { } } - my $coins_value = -"ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3A$mtx$genre$title&rft.isbn=$isbn&rft.issn=$issn&rft.aulast=$aulast&rft.aufirst=$aufirst$oauthors&rft.pub=$publisher&rft.date=$pubyear&rft.pages=$pages"; - $coins_value =~ s/(\ |&[^a])/\+/g; - $coins_value =~ s/\"/\"\;/g; -#&rft.au=&rft.btitle=&rft.date=&rft.pages=&rft.isbn=&rft.aucorp=&rft.place=&rft.pub=&rft.edition=&rft.series=&rft.genre=" + my @params = ( + [ 'ctx_ver', 'Z39.88-2004' ], + [ 'rft_val_fmt', "info:ofi/fmt:kev:mtx:$mtx" ], + [ ($mtx eq 'dc' ? 'rft.type' : 'rft.genre'), $genre ], + [ "rft.${titletype}title", $title ], + ); + + # rft.title is authorized only once, so by checking $titletype + # we ensure that rft.title is not already in the list. + if ($hosttitle and $titletype) { + push @params, [ 'rft.title', $hosttitle ]; + } + + push @params, ( + [ 'rft.isbn', $isbn ], + [ 'rft.issn', $issn ], + ); + + # If it's a subscription, these informations have no meaning. + if ($genre ne 'journal') { + push @params, ( + [ 'rft.aulast', $aulast ], + [ 'rft.aufirst', $aufirst ], + (map { [ 'rft.au', $_ ] } @authors), + [ 'rft.pub', $publisher ], + [ 'rft.date', $pubyear ], + [ 'rft.pages', $pages ], + ); + } + + my $coins_value = join( '&', + map { $$_[1] ? $$_[0] . '=' . uri_escape_utf8( $$_[1] ) : () } @params ); return $coins_value; } diff --git a/C4/XSLT.pm b/C4/XSLT.pm index c4fd6c2..a185e36 100644 --- a/C4/XSLT.pm +++ b/C4/XSLT.pm @@ -171,7 +171,9 @@ sub get_xslt_sysprefs { OPACItemLocation DisplayIconsXSLT AlternateHoldingsField AlternateHoldingsSeparator TrackClicks opacthemes IdRef OpacSuppression - OPACResultsLibrary / ) + OPACResultsLibrary OPACShowOpenURL + OpenURLResolverURL OpenURLImageLocation + OpenURLText / ) { my $sp = C4::Context->preference( $syspref ); next unless defined($sp); @@ -188,7 +190,7 @@ sub get_xslt_sysprefs { } sub XSLTParse4Display { - my ( $biblionumber, $orig_record, $xslsyspref, $fixamps, $hidden_items, $sysxml, $xslfilename, $lang ) = @_; + my ( $biblionumber, $orig_record, $xslsyspref, $fixamps, $hidden_items, $sysxml, $xslfilename, $lang, $variables ) = @_; $sysxml ||= C4::Context->preference($xslsyspref); $xslfilename ||= C4::Context->preference($xslsyspref); @@ -243,8 +245,22 @@ sub XSLTParse4Display { my $itemsxml = buildKohaItemsNamespace($biblionumber, $hidden_items); my $xmlrecord = $record->as_xml(C4::Context->preference('marcflavour')); - $xmlrecord =~ s/\<\/record\>/$itemsxml$sysxml\<\/record\>/; - if ($fixamps) { # We need to correct the HTML entities that Zebra outputs + $variables ||= {}; + if (C4::Context->preference('OPACShowOpenURL')) { + my ($biblio) = GetBiblioItemByBiblioNumber($biblionumber); + my @itypes = split( /\s/, C4::Context->preference('OPACOpenURLItemTypes') ); + if (grep /^$biblio->{itemtype}$/, @itypes) { + $variables->{COinS} = C4::Biblio::GetCOinSBiblio($orig_record); + } + } + my $varxml = "\n"; + while (my ($key, $value) = each %$variables) { + $varxml .= "$value\n"; + } + $varxml .= "\n"; + + $xmlrecord =~ s/\<\/record\>/$itemsxml$sysxml$varxml\<\/record\>/; + if ($fixamps) { # We need to correct the ampersand entities that Zebra outputs $xmlrecord =~ s/\&amp;/\&/g; $xmlrecord =~ s/\&\;lt\;/\<\;/g; $xmlrecord =~ s/\&\;gt\;/\>\;/g; diff --git a/installer/data/mysql/atomicupdate/8995_OpenURL_sysprefs.sql b/installer/data/mysql/atomicupdate/8995_OpenURL_sysprefs.sql new file mode 100644 index 0000000..2623472 --- /dev/null +++ b/installer/data/mysql/atomicupdate/8995_OpenURL_sysprefs.sql @@ -0,0 +1,6 @@ +INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES + ('OpenURLResolverURL', '', 'URL of OpenURL Resolver', NULL, 'Free'), + ('OpenURLText', '', 'Text of OpenURL links (or image title if OpenURLImageLocation is defined)', NULL, 'Free'), + ('OpenURLImageLocation', '', 'Location of image for OpenURL links', NULL, 'Free'), + ('OPACShowOpenURL', '', 'Enable display of OpenURL links in OPAC search results and detail page', NULL, 'YesNo'), + ('OPACOpenURLItemTypes', '', 'Show the OpenURL link only for these item types', NULL, 'Free'); diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index ac6f852..7726533 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -371,6 +371,11 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('OPACXSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on OPAC','Free'), ('OpenLibraryCovers','0',NULL,'If ON Openlibrary book covers will be show','YesNo'), ('OpenLibrarySearch','0',NULL,'If Yes Open Library search results will show in OPAC','YesNo'), +('OpenURLResolverURL', '', NULL, 'URL of OpenURL Resolver', 'Free'), +('OpenURLText', '', NULL, 'Text of OpenURL links (or image title if OpenURLImageLocation is defined)', 'Free'), +('OpenURLImageLocation', '', NULL, 'Location of image for OpenURL links', 'Free'), +('OPACShowOpenURL', '', NULL, 'Enable display of OpenURL links in OPAC search results and detail page', 'YesNo'), +('OPACOpenURLItemTypes', '', NULL, 'Show the OpenURL link only for these item types', 'Free'), ('OrderPdfFormat','pdfformat::layout3pages','Controls what script is used for printing (basketgroups)','','free'), ('OverDriveClientKey','','Client key for OverDrive integration','30','Free'), ('OverDriveClientSecret','','Client key for OverDrive integration','30','YesNo'), diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref index 1ca43db..bb1699f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref @@ -500,6 +500,33 @@ OPAC: - pref: OPACISBD type: textarea class: code + OpenURL: + - + - 'Complete URL of OpenURL resolver (starting with http:// or https://):' + - pref: OpenURLResolverURL + class: url + - + - 'Text of OpenURL links (or image title if OpenURLImageLocation is defined):' + - pref: OpenURLText + - + - 'Location of image for OpenURL links:' + - pref: OpenURLImageLocation + class: url + - '
Can be a complete URL starting with http:// or' + - 'https:// or the name of a file in images directory' + - '
Examples:' + - '
- http://www.example.com/img/openurl.png' + - '
- OpenURL/OpenURL.png (file is in opac-tmpl/<opacthemes>/images/OpenURL/OpenURL.png)' + - + - pref: OPACShowOpenURL + choices: + yes: Enable + no: Disable + - 'display of OpenURL link in OPAC search results and detail page.' + - + - 'List of item type codes (separated by spaces) for those you want to show the OpenURL link:' + - pref: OPACOpenURLItemTypes + - '
' Policy: - - pref: SearchMyLibraryFirst diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACDetail.xsl b/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACDetail.xsl index a8622f3..6c736e6 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACDetail.xsl +++ b/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACDetail.xsl @@ -1229,6 +1229,70 @@ + + + + + + + + + + + + + + + OpenURL + + + + + + + + + + + /opac-tmpl/prog/images/ + + + + + + + + + ? + + + + + + + OpenURL + + + + _blank + + + + + + + + + + + + + + + + + + diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACResults.xsl b/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACResults.xsl index dc66b3e..1c569ca 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACResults.xsl +++ b/koha-tmpl/opac-tmpl/bootstrap/en/xslt/MARC21slim2OPACResults.xsl @@ -443,8 +443,72 @@ -

+ + + + + + + + + + + + + + + OpenURL + + + + + + + + + + + /opac-tmpl/prog/images/ + + + + + + + + + ? + + + + + + + OpenURL + + + + _blank + + + + + + + + + + + + + + + + + + +

diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACDetail.xsl b/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACDetail.xsl index 8cb8bf9..b24a459 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACDetail.xsl +++ b/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACDetail.xsl @@ -442,6 +442,71 @@ + + + + + + + + + + + + + + + + OpenURL + + + + + + + + + + + /opac-tmpl/prog/images/ + + + + + + + + + ? + + + + + + + OpenURL + + + + _blank + + + + + + + + + + + + + + + + + + diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACResults.xsl b/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACResults.xsl index 3c97177..cdf56df 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACResults.xsl +++ b/koha-tmpl/opac-tmpl/bootstrap/en/xslt/UNIMARCslim2OPACResults.xsl @@ -84,6 +84,70 @@ + + + + + + + + + + + + + + + OpenURL + + + + + + + + + + + /opac-tmpl/prog/images/ + + + + + + + + + ? + + + + + + + OpenURL + + + + _blank + + + + + + + + + + + + + + + + + + 454 Translation of diff --git a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less index c8d9f9c..670d3c5 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less +++ b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less @@ -2503,4 +2503,8 @@ a.reviewlink:visited { cursor: pointer; } +a.OpenURL img { + vertical-align: middle; +} + @import "responsive.less"; diff --git a/opac/opac-detail.pl b/opac/opac-detail.pl index 98afe3e..146b5e4 100755 --- a/opac/opac-detail.pl +++ b/opac/opac-detail.pl @@ -1155,4 +1155,18 @@ $template->param( 'OpacLocationBranchToDisplayShelving' => C4::Context->preference('OpacLocationBranchToDisplayShelving'), ); +# OpenURL +if (C4::Context->preference('OPACShowOpenURL')) { + my ($biblio) = GetBiblioItemByBiblioNumber($biblionumber); + my @itypes = split( /\s/, C4::Context->preference('OPACOpenURLItemTypes') ); + if (grep /^$biblio->{itemtype}$/, @itypes) { + $template->param( + OPACShowOpenURL => 1, + OpenURLResolverURL => C4::Context->preference('OpenURLResolverURL'), + OpenURLText => C4::Context->preference('OpenURLText'), + OpenURLImageLocation => C4::Context->preference('OpenURLImageLocation') + ); + } +} + output_html_with_http_headers $query, $cookie, $template->output; diff --git a/opac/opac-search.pl b/opac/opac-search.pl index 64b8800..5a93cc6 100755 --- a/opac/opac-search.pl +++ b/opac/opac-search.pl @@ -586,6 +586,18 @@ if (C4::Context->preference('OpacSuppression')) { } } +# OpenURL +my @OpenURL_itypes; +if (C4::Context->preference('OPACShowOpenURL')) { + @OpenURL_itypes = split( /\s/, C4::Context->preference('OPACOpenURLItemTypes') ); + $template->param( + OPACShowOpenURL => 1, + OpenURLResolverURL => C4::Context->preference('OpenURLResolverURL'), + OpenURLText => C4::Context->preference('OpenURLText'), + OpenURLImageLocation => C4::Context->preference('OpenURLImageLocation') + ); +} + $template->param ( LIMIT_INPUTS => \@limit_inputs ); $template->param ( OPACResultsSidebar => C4::Context->preference('OPACResultsSidebar')); @@ -661,7 +673,11 @@ for (my $i=0;$i<@servers;$i++) { $res->{'incart'} = 1; } - if (C4::Context->preference('COinSinOPACResults')) { + if (C4::Context->preference('COinSinOPACResults') + or C4::Context->preference('OPACShowOpenURL')) { + if (grep /^$res->{itemtype}$/, @OpenURL_itypes) { + $res->{ShowOpenURL} = 1; + } my $record = GetMarcBiblio($res->{'biblionumber'}); $res->{coins} = GetCOinSBiblio($record); } -- 2.1.4