Bugzilla – Attachment 189474 Details for
Bug 41078
Improve handling of multiple covers on shelves/lists results in the OPAC
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 41078: Add lightbox to OPAC Lists for cover images
Bug-41078-Add-lightbox-to-OPAC-Lists-for-cover-ima.patch (text/plain), 30.19 KB, created by
Paul Derscheid
on 2025-11-11 07:00:58 UTC
(
hide
)
Description:
Bug 41078: Add lightbox to OPAC Lists for cover images
Filename:
MIME Type:
Creator:
Paul Derscheid
Created:
2025-11-11 07:00:58 UTC
Size:
30.19 KB
patch
obsolete
>From 690a3a2c752b7b6362b4046eacb64be827175a5a Mon Sep 17 00:00:00 2001 >From: David Cook <dcook@prosentient.com.au> >Date: Thu, 6 Nov 2025 05:22:04 +0000 >Subject: [PATCH] Bug 41078: Add lightbox to OPAC Lists for cover images > >This change adds the lightbox from OPAC search results to OPAC lists >for cover images. > >Test plan: >0. Enable the following system preferences: >- OPACAmazonCoverImages >- GoogleJackets >- OpenLibraryCovers >1. Using KTD, go to /cgi-bin/koha/opac-search.pl?idx=&q=test&weight_search=1 >2. Note that the lightbox for cover images which displays one cover >at a time >3. Create a public list based off those search results >4. Go to that list and note that the 3 cover images just display all >at the same time, which does not look good > >5. Apply the patch > >6. Refresh the page for the public list results >7. Note that the lightbox is now used like in OPAC search results > >Signed-off-by: Owen Leonard <oleonard@myacpl.org> >Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de> >--- > .../bootstrap/en/modules/opac-results.tt | 131 +------------- > .../bootstrap/en/modules/opac-shelves.tt | 144 ++++++--------- > .../opac-tmpl/bootstrap/js/cover_images.js | 165 ++++++++++++++++++ > 3 files changed, 221 insertions(+), 219 deletions(-) > create mode 100644 koha-tmpl/opac-tmpl/bootstrap/js/cover_images.js > >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt >index a94c8915a27..1146cffb53c 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt >@@ -486,6 +486,7 @@ > const virtualshelves = [% ( virtualshelves ) ? 1 : 0 | html %]; > </script> > [% Asset.js("js/results-list.js") | $raw %] >+ [% Asset.js("js/cover_images.js") | $raw %] > <script> > [% IF ( OpacHighlightedWords ) %] > var q_array = new Array(); // holds search terms if available >@@ -510,136 +511,6 @@ > } > [% END # /IF OpacHighlightedWords %] > >- function verify_cover_images() { >- // Loop over each container in the template which contains covers >- $(".cover-slider").each(function( index ){ >- let biblionumber = $(this).data("biblionumber"); >- let booktitle = $(this).data("title"); >- var lightbox_descriptions = []; >- $(this).find(".cover-image").each( function( index ){ >- var div = $(this); >- // Find the image in the container >- var img = div.find("img")[0]; >- if( img && $(img).length > 0 ){ >- // All slides start hidden. If this is the first one, show it. >- // Check if Amazon image is present >- if ( div.hasClass("amazon-coverimg") ) { >- let w = img.width; >- let h = img.height; >- if ((w == 1) || (h == 1)) { >- // Amazon returned single-pixel placeholder >- // Remove the container >- div.remove(); >- } else { >- lightbox_descriptions.push(_("Amazon cover image (<a href='%s'>see the original image</a>)").format($(img).data('link'))); >- } >- } else if( div.hasClass("custom-coverimg") ){ >- >- if ( (img.complete != null) && (!img.complete) || img.naturalHeight == 0 ) { >- // No image was loaded via the CustomCoverImages system preference >- // Remove the container >- div.remove(); >- } else { >- lightbox_descriptions.push(_("Custom cover image")); >- } >- } else if( div.hasClass("syndetics-coverimg") ){ >- lightbox_descriptions.push(_("Image from Syndetics")) >- } else if( div.hasClass("googlejacket-coverimg" ) ){ >- if( img.naturalHeight ){ >- lightbox_descriptions.push(_("Image from Google Books (<a href='%s'>see the original image</a>)").format($(img).data('link'))); >- } >- } else if( div.hasClass("openlibrary-coverimg") ){ >- lightbox_descriptions.push(_("Image from OpenLibrary (<a href='%s'>see the original image</a>)").format($(img).data('link'))); >- } else if( div.hasClass("coce-coverimg" ) ){ >- // Identify which service's image is being loaded by Coce >- var coce_description; >- let src = $(img).attr("src"); >- if( src.indexOf('amazon.com') >= 0 ){ >- coce_description = _("Coce image from Amazon.com"); >- } else if( src.indexOf('google.com') >= 0 ){ >- coce_description = _("Coce image from Google Books"); >- } else if( src.indexOf('openlibrary.org') >= 0 ){ >- coce_description = _("Coce image from Open Library"); >- } >- div.find(".hint").html(coce_description); >- lightbox_descriptions.push(coce_description); >- } else if ( div.hasClass("bakertaylor-coverimg" ) ){ >- lightbox_descriptions.push(_("Image from Baker & Taylor")); >- } else if ( div.hasClass("cover-image local-coverimg" ) ){ >- lightbox_descriptions.push(_("Local cover image")); >- } else { >- lightbox_descriptions.push(_("Cover image source unknown")); >- } >- } else { >- div.remove(); >- } >- }); >- >- // Lightbox for cover images >- Chocolat(this.querySelectorAll('.cover-image a'), { >- description: function(){ >- return lightbox_descriptions[this.settings.currentImageIndex]; >- } >- }); >- >- }); >- >- $(".cover-slider").each(function(){ >- var coverSlide = this; >- var coverImages = $(this).find(".cover-image"); >- if( coverImages.length > 1 ){ >- coverImages.each(function( index ){ >- // If more that one image is present, add a navigation link >- // for activating the slide >- var covernav = $("<a href=\"#\" data-num=\"" + index + "\" class=\"cover-nav\"></a>"); >- if( index == 0 ){ >- // Set the first navigation link as active >- $(covernav).addClass("nav-active"); >- } >- $(covernav).html("<i class=\"fa fa-circle\"></i>"); >- $(coverSlide).append( covernav ); >- }); >- } >- >- $(coverSlide).find(".cover-image").eq(0).show(); >- >- if( $(coverSlide).find(".cover-image").length < 1 ){ >- $(coverSlide).remove(); >- } else { >- // This is a suboptimal workaround; we should do this via load, but >- // the image code is scattered all over now. We come here now after >- // window load and wait_for_images (so load completed). >- var check_complete = 1; >- $(coverSlide).find("img").each( function() { >- if( !this.complete || this.naturalHeight == 0 ) check_complete = 0; >- }); >- if( check_complete ) $(coverSlide).removeClass('cover-slides'); >- } >- }); >- >- $(".cover-slider").on("click",".cover-nav", function(e){ >- e.preventDefault(); >- var cover_slider = $(this).parent(); >- // Adding click handler for cover image navigation links >- var num = $(this).data("num"); >- $(cover_slider).find(".cover-nav").removeClass("nav-active"); >- $(this).addClass("nav-active"); >- $(cover_slider).find(".cover-image").hide(); >- $(cover_slider).find(".cover-image").eq( num ).show(); >- }); >- >- $("#editions img").each(function(i){ >- if ( this.src.indexOf('amazon.com') >= 0 ) { >- let w = this.width; >- let h = this.height; >- if ((w == 1) || (h == 1)) { >- this.src = 'https://images-na.ssl-images-amazon.com/images/G/01/x-site/icons/no-img-sm.gif'; >- } else if ( (this.complete != null) && (!this.complete) || this.naturalHeight == 0 ) { >- this.src = 'https://images-na.ssl-images-amazon.com/images/G/01/x-site/icons/no-img-sm.gif'; >- } >- } >- }); >- } /* /verify_images */ > > > let counter_wait = 0; >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-shelves.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-shelves.tt >index 60e609a8569..0bdb569c82f 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-shelves.tt >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-shelves.tt >@@ -18,6 +18,19 @@ > [% SET ArticleRequestsEnabled = ( Koha.Preference('ArticleRequests') == 1 ) && LoginEnabled %] > [% SET MultiHolds = ( Koha.Preference('DisplayMultiPlaceHold') == 1 ) && HoldsEnabled %] > [% SET CoverImagePlugins = KohaPlugins.get_plugins_opac_cover_images %] >+[% SET OPACLocalCoverImages = Koha.Preference('OPACLocalCoverImages') %] >+[% SET OPACAmazonCoverImages = Koha.Preference('OPACAmazonCoverImages') %] >+[% SET SyndeticsCoverImages = ( Koha.Preference('SyndeticsEnabled') && Koha.Preference('SyndeticsCoverImages') ) %] >+[% SET GoogleJackets = Koha.Preference('GoogleJackets') %] >+[% SET OpenLibraryCovers = Koha.Preference('OpenLibraryCovers') %] >+[% SET BakerTaylorEnabled = Koha.Preference('BakerTaylorEnabled') %] >+[% SET OPACCoce = ( Koha.Preference('OPACCoce') && Koha.Preference('CoceProviders') ) %] >+[% IF ( CoverImagePlugins || OPACLocalCoverImages || OPACAmazonCoverImages || SyndeticsCoverImages || GoogleJackets || OpenLibraryCovers || BakerTaylorEnabled || OPACCoce || OPACCustomCoverImages ) %] >+ [% SET CoverImages = 1 %] >+ [% SET OPACCustomCoverImages = ( Koha.Preference('OPACCustomCoverImages') && Koha.Preference('CustomCoverImagesURL') ) %] >+ [% SET OverDriveEnabled = Koha.Preference('OverDriveLibraryID') && Koha.Preference('OverDriveClientKey') && Koha.Preference('OverDriveClientSecret') %] >+[% END %] >+[% SET OverDriveEnabled = ( Koha.Preference( "OPACOverDrive" ) && Koha.Preference('OverDriveLibraryID') && Koha.Preference('OverDriveClientKey') && Koha.Preference('OverDriveClientSecret') ) %] > > [% BLOCK delete_shelf %] > <form action="/cgi-bin/koha/opac-shelves.pl" method="post" id="deleteshelf[% shelf.shelfnumber | html %]" class="d-inline"> >@@ -49,6 +62,7 @@ > [% INCLUDE 'doc-head-close.inc' %] > [% BLOCK cssinclude %] > [% END %] >+[% Asset.css("lib/Chocolat/css/chocolat.css") | $raw %] > </head> > [% IF ( loggedinusername ) %] > [% INCLUDE 'bodytag.inc' bodyid='opac-userlists' bodyclass='scrollto' %] >@@ -407,95 +421,7 @@ > </td> > [% END %] > <td> >- <div class="coverimages"> >- [% IF ( itemsloo.title ) %] >- [% img_title = itemsloo.title %] >- [% ELSE %] >- [% img_title = itemsloo.biblionumber %] >- [% END %] >- <a >- class="p1" >- href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% itemsloo.biblionumber | html %]" >- data-isbn="[% itemsloo.normalized_isbn | html %]" >- data-title="[% img_title | html %]" >- > >- [% IF ( OPACLocalCoverImages ) %] >- <span title="[% img_title | html %]" class="[% itemsloo.biblionumber | html %]" id="local-thumbnail[% loop.count | html %]"></span> >- [% END %] >- >- [% IF ( OPACAmazonCoverImages ) %] >- [% IF ( itemsloo.normalized_isbn ) %] >- <span title="[% img_title | html %]" id="amazon-thumbnail[% loop.count | html %]" >- ><img src="https://images-na.ssl-images-amazon.com/images/P/[% itemsloo.normalized_isbn | html %].01.TZZZZZZZ.jpg" alt="" >- /></span> >- [% ELSE %] >- <span class="no-image">No cover image available</span> >- [% END %] >- [% END %] >- >- [% IF ( SyndeticsEnabled && SyndeticsCoverImages ) %] >- <span title="[% img_title | html %]" id="syndetics-thumbnail[% loop.count | html %]" >- ><img >- src="https://secure.syndetics.com/index.aspx?isbn=[% itemsloo.normalized_isbn | html %]/[% SyndeticsCoverImageSize | uri %].GIF&client=[% SyndeticsClientCode | html %]&type=xw10&upc=[% itemsloo.normalized_upc | html %]&oclc=[% itemsloo.normalized_oclc | html %]" >- alt="" >- /></span> >- [% END %] >- >- [% IF ( GoogleJackets ) %] >- [% IF ( itemsloo.normalized_isbn ) %] >- <div title="[% img_title | html %]" class="[% itemsloo.normalized_isbn | html %]" id="gbs-thumbnail[% loop.count | html %]"></div> >- [% ELSE %] >- <span class="no-image">No cover image available</span> >- [% END %] >- [% END %] >- >- [% IF ( Koha.Preference('OpacCoce') && Koha.Preference('CoceProviders') ) %] >- [% coce_id = itemsloo.normalized_ean || itemsloo.normalized_isbn %] >- [% IF ( coce_id ) %] >- <span title="[% img_title | html %]" class="[% coce_id | html %]" id="coce-thumbnail[% loop.count | html %]"></span> >- [% ELSE %] >- <span class="no-image">No cover image available</span> >- [% END %] >- [% END %] >- >- [% IF OpenLibraryCovers %] >- [% IF itemsloo.normalized_isbn %] >- <span title="[% img_title | html %]" class="[% itemsloo.normalized_isbn | html %]" id="openlibrary-thumbnail[% loop.count | html %]"></span> >- [% ELSE %] >- <span class="no-image">No cover image available</span> >- [% END %] >- [% END %] >- </a> >- <!-- / .p1 --> >- [% IF ( Koha.Preference('BakerTaylorEnabled') && !Koha.Preference('BakerTaylorBookstoreURL') ) %] >- [% bt_id = ( itemsloo.normalized_upc || itemsloo.normalized_isbn ) %] >- [% IF ( bt_id ) %] >- <img alt="See Baker & Taylor" src="[% BakerTaylorImageURL | html %][% bt_id | html %]" /> >- [% ELSE %] >- <span class="no-image">No cover image available</span> >- [% END %] >- [% END %] >- >- [% IF ( Koha.Preference('BakerTaylorEnabled') && Koha.Preference('BakerTaylorBookstoreURL') ) %] >- [% bt_id = ( itemsloo.normalized_upc || itemsloo.normalized_isbn ) %] >- [% IF ( bt_id ) %] >- <a href="https://[% Koha.Preference('BakerTaylorBookstoreURL') | uri %][% bt_id | uri %]"> >- <img alt="See Baker & Taylor" src="[% BakerTaylorImageURL | html %][% bt_id | html %]" /> >- </a> >- [% ELSE %] >- <span class="no-image">No cover image available</span> >- [% END %] >- [% END %] >- >- [% IF Koha.Preference('OPACCustomCoverImages') AND Koha.Preference('CustomCoverImagesURL') %] >- [% SET custom_cover_image_url = itemsloo.biblio_object.custom_cover_image_url %] >- [% IF custom_cover_image_url %] >- <a class="custom_cover_image" href="[% PROCESS biblio_a_href biblionumber => itemsloo.biblionumber %]"> >- <img alt="Cover image" src="[% custom_cover_image_url | url %]" >- /></a> >- [% END %] >- [% END %] >- </div> >+ <div class="coverimages"> [% INCLUDE 'cover-images.inc' SEARCH_RESULT=itemsloo %] </div> > > [% itemsloo.XSLTBloc | $raw %] > >@@ -889,6 +815,7 @@ > > [% INCLUDE 'opac-bottom.inc' %] > [% BLOCK jsinclude %] >+ [% Asset.js("lib/Chocolat/js/chocolat.js") | $raw %] > [% CoverImagePlugins | $raw %] > [% IF OpenLibraryCovers || OpenLibrarySearch %] > [% Asset.js("js/openlibrary.js") | $raw %] >@@ -899,7 +826,46 @@ > const virtualshelves = [% ( virtualshelves ) ? 1 : 0 | html %]; > </script> > [% Asset.js("js/results-list.js") | $raw %] >+ [% Asset.js("js/cover_images.js") | $raw %] > <script> >+ let counter_wait = 0; >+ function wait_for_images(cb){ >+ >+ var loaded = 1; >+ counter_wait++; >+ >+ [% IF GoogleJackets %] >+ if ( loaded ) { >+ loaded = KOHA.Google.done; >+ } >+ [% END %] >+ >+ [% IF OpenLibraryCovers %] >+ if ( loaded ) { >+ loaded = KOHA.OpenLibrary.done; >+ } >+ [% END %] >+ >+ [% IF ( Koha.Preference('OpacCoce') && Koha.Preference('CoceProviders') ) %] >+ if ( loaded ) { >+ loaded = KOHA.coce.done; >+ } >+ [% END %] >+ >+ if (!loaded && counter_wait < 50) {// Do not wait more than 5 seconds >+ window.setTimeout(function(){wait_for_images(cb);}, 100); >+ } else { >+ if (counter_wait >= 50 ) { >+ } >+ cb(); >+ } >+ } >+ >+ $(window).load(function() { >+ wait_for_images(verify_cover_images); >+ }); >+ >+ > [% IF ( TagsInputEnabled && loggedinusername ) %] > function tagSelected() { > var checkedBoxes = $("input:checkbox:checked"); >diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/cover_images.js b/koha-tmpl/opac-tmpl/bootstrap/js/cover_images.js >new file mode 100644 >index 00000000000..841612b9896 >--- /dev/null >+++ b/koha-tmpl/opac-tmpl/bootstrap/js/cover_images.js >@@ -0,0 +1,165 @@ >+function verify_cover_images() { >+ // Loop over each container in the template which contains covers >+ $(".cover-slider").each(function (index) { >+ let biblionumber = $(this).data("biblionumber"); >+ let booktitle = $(this).data("title"); >+ var lightbox_descriptions = []; >+ $(this) >+ .find(".cover-image") >+ .each(function (index) { >+ var div = $(this); >+ // Find the image in the container >+ var img = div.find("img")[0]; >+ if (img && $(img).length > 0) { >+ // All slides start hidden. If this is the first one, show it. >+ // Check if Amazon image is present >+ if (div.hasClass("amazon-coverimg")) { >+ let w = img.width; >+ let h = img.height; >+ if (w == 1 || h == 1) { >+ // Amazon returned single-pixel placeholder >+ // Remove the container >+ div.remove(); >+ } else { >+ lightbox_descriptions.push( >+ _( >+ "Amazon cover image (<a href='%s'>see the original image</a>)" >+ ).format($(img).data("link")) >+ ); >+ } >+ } else if (div.hasClass("custom-coverimg")) { >+ if ( >+ (img.complete != null && !img.complete) || >+ img.naturalHeight == 0 >+ ) { >+ // No image was loaded via the CustomCoverImages system preference >+ // Remove the container >+ div.remove(); >+ } else { >+ lightbox_descriptions.push(_("Custom cover image")); >+ } >+ } else if (div.hasClass("syndetics-coverimg")) { >+ lightbox_descriptions.push(_("Image from Syndetics")); >+ } else if (div.hasClass("googlejacket-coverimg")) { >+ if (img.naturalHeight) { >+ lightbox_descriptions.push( >+ _( >+ "Image from Google Books (<a href='%s'>see the original image</a>)" >+ ).format($(img).data("link")) >+ ); >+ } >+ } else if (div.hasClass("openlibrary-coverimg")) { >+ lightbox_descriptions.push( >+ _( >+ "Image from OpenLibrary (<a href='%s'>see the original image</a>)" >+ ).format($(img).data("link")) >+ ); >+ } else if (div.hasClass("coce-coverimg")) { >+ // Identify which service's image is being loaded by Coce >+ var coce_description; >+ let src = $(img).attr("src"); >+ if (src.indexOf("amazon.com") >= 0) { >+ coce_description = _("Coce image from Amazon.com"); >+ } else if (src.indexOf("google.com") >= 0) { >+ coce_description = _( >+ "Coce image from Google Books" >+ ); >+ } else if (src.indexOf("openlibrary.org") >= 0) { >+ coce_description = _( >+ "Coce image from Open Library" >+ ); >+ } >+ div.find(".hint").html(coce_description); >+ lightbox_descriptions.push(coce_description); >+ } else if (div.hasClass("bakertaylor-coverimg")) { >+ lightbox_descriptions.push( >+ _("Image from Baker & Taylor") >+ ); >+ } else if (div.hasClass("cover-image local-coverimg")) { >+ lightbox_descriptions.push(_("Local cover image")); >+ } else { >+ lightbox_descriptions.push( >+ _("Cover image source unknown") >+ ); >+ } >+ } else { >+ div.remove(); >+ } >+ }); >+ >+ // Lightbox for cover images >+ Chocolat(this.querySelectorAll(".cover-image a"), { >+ description: function () { >+ return lightbox_descriptions[this.settings.currentImageIndex]; >+ }, >+ }); >+ }); >+ >+ $(".cover-slider").each(function () { >+ var coverSlide = this; >+ var coverImages = $(this).find(".cover-image"); >+ if (coverImages.length > 1) { >+ coverImages.each(function (index) { >+ // If more that one image is present, add a navigation link >+ // for activating the slide >+ var covernav = $( >+ '<a href="#" data-num="' + >+ index + >+ '" class="cover-nav"></a>' >+ ); >+ if (index == 0) { >+ // Set the first navigation link as active >+ $(covernav).addClass("nav-active"); >+ } >+ $(covernav).html('<i class="fa fa-circle"></i>'); >+ $(coverSlide).append(covernav); >+ }); >+ } >+ >+ $(coverSlide).find(".cover-image").eq(0).show(); >+ >+ if ($(coverSlide).find(".cover-image").length < 1) { >+ $(coverSlide).remove(); >+ } else { >+ // This is a suboptimal workaround; we should do this via load, but >+ // the image code is scattered all over now. We come here now after >+ // window load and wait_for_images (so load completed). >+ var check_complete = 1; >+ $(coverSlide) >+ .find("img") >+ .each(function () { >+ if (!this.complete || this.naturalHeight == 0) >+ check_complete = 0; >+ }); >+ if (check_complete) $(coverSlide).removeClass("cover-slides"); >+ } >+ }); >+ >+ $(".cover-slider").on("click", ".cover-nav", function (e) { >+ e.preventDefault(); >+ var cover_slider = $(this).parent(); >+ // Adding click handler for cover image navigation links >+ var num = $(this).data("num"); >+ $(cover_slider).find(".cover-nav").removeClass("nav-active"); >+ $(this).addClass("nav-active"); >+ $(cover_slider).find(".cover-image").hide(); >+ $(cover_slider).find(".cover-image").eq(num).show(); >+ }); >+ >+ $("#editions img").each(function (i) { >+ if (this.src.indexOf("amazon.com") >= 0) { >+ let w = this.width; >+ let h = this.height; >+ if (w == 1 || h == 1) { >+ this.src = >+ "https://images-na.ssl-images-amazon.com/images/G/01/x-site/icons/no-img-sm.gif"; >+ } else if ( >+ (this.complete != null && !this.complete) || >+ this.naturalHeight == 0 >+ ) { >+ this.src = >+ "https://images-na.ssl-images-amazon.com/images/G/01/x-site/icons/no-img-sm.gif"; >+ } >+ } >+ }); >+} /* /verify_images */ >-- >2.39.5
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 41078
:
189142
|
189246
|
189319
|
189320
| 189474 |
189475