Browse Source

Bug 25846: Improve handling of multiple covers on catalog search results in the staff client

This patch modifies the template, JS, and CSS for the staff
interface catalog search results in order to gracefully handle multiple
cover images.

The changed version loops through any cover images which might be
embedded and checks that they are successfully loaded. Only
successfully-loaded images are shown. Only the first image is shown, and
the others can be "paged through" using generated navigation controls.

To test, apply the page and rebuild the staff client CSS
(https://wiki.koha-community.org/wiki/Working_with_SCSS_in_the_OPAC_and_staff_client).

Enable multiple cover image services. The patch was developed with these
services available:

 - Amazon
 - Local cover images (including multiple local cover images)
 - Coce (serving up Amazon, Google, and OpenLibrary images)
 - Images from the CustomCoverImages preference

Perform a variet of searches and confirm that cover images are
displaying correctly, whether there be 0, 1, 2, or more covers
available for each.

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
21.05.x
Owen Leonard 3 years ago
committed by Jonathan Druart
parent
commit
927a62ba6f
  1. 6
      koha-tmpl/intranet-tmpl/js/coce.js
  2. 17
      koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss
  3. 91
      koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tt
  4. 144
      koha-tmpl/intranet-tmpl/prog/js/pages/results.js

6
koha-tmpl/intranet-tmpl/js/coce.js

@ -14,9 +14,9 @@ KOHA.coce = {
* and run a search with all collected isbns to coce cover service.
* The result is asynchronously returned, and used to append <img>.
*/
getURL: function(host, provider, newWindow) {
getURL: function(host, provider) {
var ids = [];
$("[id^=coce-thumbnail]").each(function(i) {
$("[id^=coce-thumbnail]").each(function() {
var id = $(this).attr("class"); // id=isbn
if (id !== '') { ids.push(id); }
});
@ -36,7 +36,7 @@ KOHA.coce = {
img.onload = function() {
// image dimensions can't be known until image has loaded
if (img.height == 1 && img.width == 1) {
$(this).remove();
$(this).closest(".coce-coverimg").remove();
}
};
$(this).html(img);

17
koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss

@ -2178,7 +2178,7 @@ li {
}
.cover-slides {
background: #FFF url("[% interface | html %]/[% theme | html %]/img/spinner-small.gif") center center no-repeat;
background: #FFF url("../img/spinner-small.gif") center center no-repeat;
.hint {
font-size: 90%;
@ -2195,6 +2195,21 @@ li {
}
}
td {
&.bookcoverimg {
background: #FFF url("../img/spinner-small.gif") center center no-repeat;
min-width: 120px;
text-align: center;
}
.cover-slides {
background: transparent none;
border: 0;
margin: 0;
min-height: unset;
padding: 0;
}
}
.cover-image {
display: none;

91
koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tt

@ -420,46 +420,61 @@
<!-- Actual Search Results -->
[% SET MaxSearchResultsItemsPerRecordStatusCheck = Koha.Preference('MaxSearchResultsItemsPerRecordStatusCheck') %]
[% FOREACH SEARCH_RESULT IN SEARCH_RESULTS %]
<tr>
<tr id="row[% SEARCH_RESULT.biblionumber | html %]">
[% IF ( AmazonCoverImages || LocalCoverImages || AdlibrisEnabled || IntranetCoce || (Koha.Preference('CustomCoverImages') && Koha.Preference('CustomCoverImagesURL')) ) %]
<td>
[% IF ( LocalCoverImages) %]
<a class="p1" href="/cgi-bin/koha/catalogue/[% DetailPage | html %]?biblionumber=[% SEARCH_RESULT.biblionumber |url %]">
<span title="[% SEARCH_RESULT.biblionumber |url %]" class="[% SEARCH_RESULT.biblionumber | html %]" id="local-thumbnail[% loop.count | html %]"></span>
</a>
[% END %]
[% IF ( AdlibrisEnabled && SEARCH_RESULT.normalized_isbn ) %]
<a href="[% AdlibrisURL | url %]?isbn=[% SEARCH_RESULT.normalized_isbn | uri %]"><img src="[% AdlibrisURL | url %]?isbn=[% SEARCH_RESULT.normalized_isbn | uri %]" class="adlibris-cover" alt="Adlibris cover image" /></a>
[% END %]
[% IF ( AmazonCoverImages ) %]
[% IF ( SEARCH_RESULT.normalized_isbn ) %]
<a class="p1" href="/cgi-bin/koha/catalogue/[% DetailPage | html %]?biblionumber=[% SEARCH_RESULT.biblionumber |url %]">
<img src="https://images-na.ssl-images-amazon.com/images/P/[% SEARCH_RESULT.normalized_isbn | html %].01.TZZZZZZZ.jpg" alt="" class="thumbnail" />
[% ELSIF ( !LocalCoverImages ) %]
<a class="p1" href="/cgi-bin/koha/catalogue/[% DetailPage | html %]?biblionumber=[% SEARCH_RESULT.biblionumber |url %]">
<span class="no-image">No cover image available</span>
[% END %]
</a>
[% END # /IF AmazonCoverImages %]
[% IF ( IntranetCoce && CoceProviders ) %]
[% coce_id = SEARCH_RESULT.normalized_ean || SEARCH_RESULT.normalized_isbn %]
<a class="p1" href="/cgi-bin/koha/catalogue/[% DetailPage | html %]?biblionumber=[% SEARCH_RESULT.biblionumber | url %]">
[% IF ( coce_id ) %]
<span style="block" title="[% SEARCH_RESULT.biblionumber | url %]" class="[% coce_id | html %]" id="coce-thumbnail[% loop.count | html %]"></span>
[% ELSE %]
<span class="no-image">No cover image available</span>
[% END %]
</a>
[% END %]
<td class="bookcoverimg">
<div id="cover-slides-[% SEARCH_RESULT.biblionumber | html %]" class="cover-slides" data-biblionumber="[% SEARCH_RESULT.biblionumber | html %]">
[% IF ( LocalCoverImages ) %][% SEARCH_RESULT.localimage | html %]
<div id="local-coverimg-[% SEARCH_RESULT.biblionumber | html %]" class="cover-image local-coverimg">
<a class="p1" href="/cgi-bin/koha/catalogue/[% DetailPage | html %]?biblionumber=[% SEARCH_RESULT.biblionumber |url %]">
<img src="/cgi-bin/koha/catalogue/image.pl?thumbnail=1&amp;biblionumber=[% SEARCH_RESULT.biblionumber | uri %]" alt="Local cover image" />
</a>
<div class="hint">Local cover image</div>
</div>
[% END %]
[% IF Koha.Preference('CustomCoverImages') && Koha.Preference('CustomCoverImagesURL') %]
[% SET custom_cover_image_url = SEARCH_RESULT.biblio_object.custom_cover_image_url %]
[% IF custom_cover_image_url %]
<a class="custom_cover_image" href="[% custom_cover_image_url | url %]"><img alt="Cover image" src="[% custom_cover_image_url | url %]" />
[% END %]
[% END %]
</td>
[% END # /IF( AmazonCoverImages || LocalCoverImages || AdlibrisEnabled || IntranetCoce )%]
[% IF ( AdlibrisEnabled && SEARCH_RESULT.normalized_isbn ) %]
<div id="adlibris-coverimg-[% SEARCH_RESULT.biblionumber | html %]" class="cover-image adlibris-coverimg">
<a href="[% AdlibrisURL | url %]?isbn=[% SEARCH_RESULT.normalized_isbn | uri %]"><img src="[% AdlibrisURL | url %]?isbn=[% SEARCH_RESULT.normalized_isbn | uri %]" class="adlibris-cover" alt="Adlibris cover image" /></a>
</div>
<div class="hint">Adlibris cover image</div>
[% END %]
[% IF ( AmazonCoverImages && SEARCH_RESULT.normalized_isbn ) %]
<div id="amazon-bookcoverimg-[% SEARCH_RESULT.biblionumber | html %]" class="cover-image amazon-bookcoverimg">
<a class="p1" href="/cgi-bin/koha/catalogue/[% DetailPage | html %]?biblionumber=[% SEARCH_RESULT.biblionumber |url %]">
<img src="https://images-na.ssl-images-amazon.com/images/P/[% SEARCH_RESULT.normalized_isbn | html %].01.TZZZZZZZ.jpg" alt="Cover image from Amazon.com" />
</a>
<div class="hint">Amazon cover image</div>
</div>
[% END # /IF AmazonCoverImages %]
[% IF ( IntranetCoce && CoceProviders && SEARCH_RESULT.normalized_isbn ) %]
[% coce_id = SEARCH_RESULT.normalized_ean || SEARCH_RESULT.normalized_isbn %]
[% IF ( coce_id ) %]
<div id="coce-coverimg-[% SEARCH_RESULT.biblionumber | html %]" class="cover-image coce-coverimg">
<a class="p1" href="/cgi-bin/koha/catalogue/[% DetailPage | html %]?biblionumber=[% SEARCH_RESULT.biblionumber | url %]">
<span style="block" title="[% SEARCH_RESULT.biblionumber | url %]" class="[% coce_id | html %]" id="coce-thumbnail[% SEARCH_RESULT.biblionumber | html %]"></span>
</a>
<div class="hint">Coce cover image</div>
</div>
[% END %]
[% END %]
[% IF Koha.Preference('CustomCoverImages') && Koha.Preference('CustomCoverImagesURL') %]
[% SET custom_cover_image_url = SEARCH_RESULT.biblio_object.custom_cover_image_url %]
[% IF custom_cover_image_url %]
<div id="custom-coverimg-[% SEARCH_RESULT.biblionumber | html %]" class="cover-image custom-coverimg">
<a class="custom_cover_image" href="[% custom_cover_image_url | url %]">
<img alt="Custom cover image" src="[% custom_cover_image_url | url %]" />
</a>
<div class="hint">Custom cover image</div>
</div>
[% END %]
[% END %]
</div>
</td>
[% END # /IF( AmazonCoverImages || LocalCoverImages || AdlibrisEnabled || IntranetCoce )%]
<td>
<input type="checkbox" class="selection" id="bib[% SEARCH_RESULT.biblionumber | html %]" name="biblionumber" value="[% SEARCH_RESULT.biblionumber | html %]" style="display:none" />

144
koha-tmpl/intranet-tmpl/prog/js/pages/results.js

@ -1,17 +1,128 @@
/* global KOHA biblionumber new_results_browser addMultiple vShelfAdd openWindow search_result SEARCH_RESULTS PREF_AmazonCoverImages PREF_LocalCoverImages PREF_IntranetCoce PREF_CoceProviders CoceHost CoceProviders addRecord delSingleRecord PREF_BrowseResultSelection resetSearchContext addBibToContext delBibToContext getContextBiblioNumbers holdfor_cardnumber holdforclub strQuery PREF_NotHighlightedWords __ */
/* global KOHA biblionumber new_results_browser addMultiple vShelfAdd openWindow search_result SEARCH_RESULTS PREF_LocalCoverImages PREF_IntranetCoce PREF_CoceProviders CoceHost CoceProviders addRecord delSingleRecord PREF_BrowseResultSelection resetSearchContext addBibToContext delBibToContext getContextBiblioNumbers holdfor_cardnumber holdforclub strQuery PREF_NotHighlightedWords __ */
if( PREF_AmazonCoverImages ){
$(window).load(function() {
verify_images();
function verify_images() {
/* Loop over each container in the template which contains covers */
var coverSlides = $(".cover-slides"); /* One coverSlides for each search result */
coverSlides.each( function( index ){
var slide = $(this);
var biblionumber = $(this).data("biblionumber");
var coverImages = $(".cover-image", slide ); /* Multiple coverImages for each coverSlides */
var blanks = [];
coverImages.each( function( index ){
var div = $(this);
var coverId = div.attr("id");
/* Find the image in the container */
var img = div.find("img")[0];
if( $(img).length > 0 ){
if( (img.complete != null) && (!img.complete) || img.naturalHeight == 0 ){
/* No image loaded in the container. Remove the slide */
blanks.push( coverId );
div.remove();
} else {
/* Check if Amazon image is present */
if ( div.hasClass("amazon-bookcoverimg") ) {
w = img.width;
h = img.height;
if ((w == 1) || (h == 1)) {
/* Amazon returned single-pixel placeholder */
/* Remove the container */
blanks.push( coverId );
div.remove();
}
}
/* Check if Local image is present */
if ( div.hasClass("local-coverimg" ) ) {
w = img.width;
h = img.height;
if ((w == 1) || (h == 1)) {
/* Local cover image returned single-pixel placeholder */
/* Remove the container */
blanks.push( coverId );
div.remove();
}
}
if( div.hasClass("custom-img") ){
/* Check for image from CustomCoverImages system preference */
if ( (img.complete != null) && (!img.complete) || img.naturalHeight == 0 ) {
/* No image was loaded via the CustomCoverImages system preference */
/* Remove the container */
blanks.push( coverId );
div.remove();
}
}
if( div.hasClass("coce-coverimg") ){
/* Identify which service's image is being loaded by IntranetCoce system pref */
if( $(img).attr("src").indexOf('amazon.com') >= 0 ){
div.find(".hint").html(_("Coce image from Amazon.com"));
} else if( $(img).attr("src").indexOf('google.com') >= 0 ){
div.find(".hint").html(_("Coce image from Google Books"));
} else if( $(img).attr("src").indexOf('openlibrary.org') >= 0 ){
div.find(".hint").html(_("Coce image from Open Library"));
} else {
blanks.push( coverId );
div.remove();
}
}
if( coverImages.length > 1 ){
if( blanks.includes( coverId ) ){
/* Don't add covernav link */
} else {
var covernav = $("<a href=\"#\" data-coverid=\"" + coverId + "\" data-biblionumber=\"" + biblionumber + "\" class=\"cover-nav\"></a>");
$(covernav).html("<i class=\"fa fa-circle\"></i>");
slide.addClass("cover-slides").append( covernav );
}
}
} /* /IF image loaded */
} else {
blanks.push( coverId );
div.remove();
} /* /IF there is an image tag */
/* console.log( coverImages ); */
});
/* Show the first cover image slide after empty ones have been removed */
$(".cover-image", slide).eq(0).show();
/* Remove "loading" background gif */
$(".bookcoverimg").css("background","unset");
if( $(".cover-image", slide).length < 2 ){
/* Don't show controls for switching between covers if there is only 1 */
$(".cover-nav", slide).remove();
}
/* Set the first navigation link as active */
$(".cover-nav", slide).eq(0).addClass("nav-active");
/* If no slides contain any cover images, remove the container */
if( $(".cover-image:visible", slide).length < 1 ){
slide.remove();
}
});
}
$(window).load(function() {
verify_images();
});
var Sticky;
var toHighlight = {};
var q_array;
$(document).ready(function() {
$("#searchresults").on("click",".cover-nav", function(e){
e.preventDefault();
/* Adding click handler for cover image navigation links */
var coverid = $(this).data("coverid");
var biblionumber = $(this).data("biblionumber");
var slides = $("#cover-slides-" + biblionumber );
$(".cover-nav", slides).removeClass("nav-active");
$(this).addClass("nav-active");
$(".cover-image", slides).hide();
$( "#" + coverid ).show();
});
$(".moretoggle").click(function(e) {
e.preventDefault();
$(this).siblings(".collapsible-facet").toggle();
@ -86,8 +197,8 @@ $(document).ready(function() {
if( search_result.query_desc ){
toHighlight = $("p,span.results_summary,a.title");
q_array = search_result.query_desc.split(" ");
// ensure that we don't have "" at the end of the array, which can
// break the highlighter
/* ensure that we don't have "" at the end of the array, which can */
/* break the highlighter */
while ( q_array.length > 0 && q_array[q_array.length-1] == "") {
q_array = q_array.splice(0,-1);
}
@ -303,21 +414,6 @@ function holdForClub() {
placeHold();
}
// http://www.oreillynet.com/pub/a/javascript/2003/10/21/amazonhacks.html
function verify_images() {
$("img").each(function(){
if ((this.src.indexOf('images-amazon.com') >= 0) || (this.src.indexOf('images.amazon.com') >=0)) {
var w = this.width;
var h = this.height;
if ((w == 1) || (h == 1)) {
$(this).parent().html('<span class="no-image">'+ __("No cover image available") +'</span>');
} else if ((this.complete != null) && (!this.complete)) {
$(this).parent().html('<span class="no-image">'+ __("No cover image available") +'</span>');
}
}
});
}
function toggleBatchOp( b ){
var results_batch_ops = $("#results_batch_ops");
if( b ){
@ -332,7 +428,7 @@ function resultsBatchProcess( op ){
var params = [];
var url = "";
if( op == "edit" ){
// batch edit selected records
/* batch edit selected records */
if ( selected.length < 1 ){
alert( __("You must select at least one record") );
} else {
@ -343,7 +439,7 @@ function resultsBatchProcess( op ){
location.href = url;
}
} else if( op == "delete" ){
// batch delete selected records
/* batch delete selected records */
if ( selected.length < 1) {
alert( __("You must select at least one record") );
} else {
@ -354,7 +450,7 @@ function resultsBatchProcess( op ){
location.href = url;
}
} else if( op == "merge" ){
// merge selected records
/* merge selected records */
if ( selected.length < 2) {
alert( __("At least two records must be selected for merging") );
} else {

Loading…
Cancel
Save