From caf6bef55b8eafed8da971ed126441b9f37bd6f0 Mon Sep 17 00:00:00 2001 From: Megan Schanz Date: Thu, 20 Aug 2026 16:11:33 -0400 Subject: [PATCH 1/6] Create a new cover loader for EDS to proxy the image provided from the EDS API --- config/vufind/config.ini | 3 +- .../VuFind/src/VuFind/Content/Covers/EDS.php | 115 ++++++++++++++++++ .../src/VuFind/Content/Covers/EDSFactory.php | 70 +++++++++++ .../VuFind/Content/Covers/PluginManager.php | 1 + .../Content/Covers/vufind-exception.log | 0 module/VuFind/src/VuFind/RecordDriver/EDS.php | 51 +++++++- .../src/VuFind/RecordDriver/EDSFactory.php | 73 +++++++++++ .../src/VuFind/RecordDriver/PluginManager.php | 2 +- 8 files changed, 310 insertions(+), 5 deletions(-) create mode 100644 module/VuFind/src/VuFind/Content/Covers/EDS.php create mode 100644 module/VuFind/src/VuFind/Content/Covers/EDSFactory.php create mode 100644 module/VuFind/src/VuFind/Content/Covers/vufind-exception.log create mode 100644 module/VuFind/src/VuFind/RecordDriver/EDSFactory.php diff --git a/config/vufind/config.ini b/config/vufind/config.ini index e99520ebc0c0..bb50b02691b7 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -1098,7 +1098,7 @@ verify_server_certificate = false ; coversize setting to false: ;coversize = false -; You can select Syndetics, LibraryThing, Summon, OpenLibrary, +; You can select Syndetics, LibraryThing, Summon, OpenLibrary, EDS, ; Contentcafe, Buchhandel, Google, BrowZine, ObalkyKnih, Orb, Koha, Demo, ; and/or LocalFile. Service-specific notes: ; - BrowZine requires you to have BrowZine.ini configured appropriately. @@ -1139,6 +1139,7 @@ verify_server_certificate = false ; - Orb requires that you complete the [Orb] section. Cache settings can be ; adjusted in the [Cache_OrbCover] section. ; - Summon service takes a Serials Solutions client key, NOT Summon API key! +; - EDS should be used when the EDS.ini is provided to use cover images from the EDS API ;coverimages = Syndetics:MySyndeticsId,LibraryThing:MyLibraryThingId,Google,ObalkyKnih,OpenLibrary,Summon:MySerialsSolutionsClientKey,Buchhandel,Contentcafe:MyContentCafeID,BrowZine,LocalFile:PathToFile,Koha,Orb ; When using the Koha cover provider, you should fill in this setting: diff --git a/module/VuFind/src/VuFind/Content/Covers/EDS.php b/module/VuFind/src/VuFind/Content/Covers/EDS.php new file mode 100644 index 000000000000..01fd067b49c8 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Covers/EDS.php @@ -0,0 +1,115 @@ +. + * + * @category VuFind + * @package Content + * @author Megan Schanz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ + +namespace VuFind\Content\Covers; + +use Laminas\Cache\Storage\StorageInterface; + +/** + * EDS cover content loader. + * + * @category VuFind + * @package Content + * @author Megan Schanz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class EDS extends \VuFind\Content\AbstractCover implements + \Psr\Log\LoggerAwareInterface, + \VuFind\Http\CachingDownloaderAwareInterface +{ + use \VuFind\Log\LoggerAwareTrait; + use \VuFind\Http\CachingDownloaderAwareTrait; + use \VuFind\Cache\CacheTrait; + + /** + * Constructor + * + * @param StorageInterface $cache Cache + */ + public function __construct(StorageInterface $cache) + { + $this->supportsRecordid = $this->cacheAllowed = true; + $this->setCacheStorage($cache); + } + + /** + * Set the key to store in the cache to share between EDS cover loader and record driver + * + * @param string $key Key to put in the cache + * + * @return string The determined key + */ + protected function getCacheKey($key = '') + { + return 'EDS_Shared_' . md5($key); + } + + /** + * Determine if this handler supports the provided identifiers. + * + * In this case, we look for the recordid and source keys + * that are required for proxying the image and making sure the source + * is EDS. + * + * @param array $ids Array of identifiers (recordid, isbn, etc.) + * + * @return bool + */ + public function supports($ids) + { + return isset($ids['recordid']) + && isset($ids['source']) + && $ids['source'] === 'EDS'; + } + + /** + * Get an image URL for the specific record. + * + * This is the primary method used by the Cover Loader manager. + * + * @param string $key Cover provider key (e.g. 'eds') + * @param string $size Size of image requested + * @param array $ids Array of identifiers + * + * @return string|bool URL of the image or false if unavailable + */ + public function getUrl($key, $size, $ids) + { + $recordId = $ids['recordid'] ?? ''; + $url = $this->getCachedData($recordId); + + if (str_starts_with($url, 'http://') || str_starts_with($url, 'https://')) { + $this->debug('Returning Cover image URL: ' . $url); + return $url; + } + + return false; + } +} diff --git a/module/VuFind/src/VuFind/Content/Covers/EDSFactory.php b/module/VuFind/src/VuFind/Content/Covers/EDSFactory.php new file mode 100644 index 000000000000..a7587f56a500 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Covers/EDSFactory.php @@ -0,0 +1,70 @@ +. + * + * @category VuFind + * @package Content + * @author Megan Schanz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ + +namespace VuFind\Content\Covers; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for EDS cover loader. + * + * PHP version 8 + * + * @category VuFind + * @package Content + * @author Megan Schanz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/vufind/ Main page + */ +class EDSFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException&\Throwable if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, array $options = null) + { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + + return new $requestedName($container->get(\VuFind\Cache\Manager::class)->getCache('object')); + } +} diff --git a/module/VuFind/src/VuFind/Content/Covers/PluginManager.php b/module/VuFind/src/VuFind/Content/Covers/PluginManager.php index 15ecebb036b2..0e145c8b5ff1 100644 --- a/module/VuFind/src/VuFind/Content/Covers/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/Covers/PluginManager.php @@ -58,6 +58,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager 'browzine' => BrowZine::class, 'contentcafe' => ContentCafe::class, 'demo' => Demo::class, + 'eds' => EDS::class, 'google' => Google::class, 'koha' => Koha::class, 'librarything' => LibraryThing::class, diff --git a/module/VuFind/src/VuFind/Content/Covers/vufind-exception.log b/module/VuFind/src/VuFind/Content/Covers/vufind-exception.log new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/module/VuFind/src/VuFind/RecordDriver/EDS.php b/module/VuFind/src/VuFind/RecordDriver/EDS.php index 8b4137565aab..6f0d0cc64bd7 100644 --- a/module/VuFind/src/VuFind/RecordDriver/EDS.php +++ b/module/VuFind/src/VuFind/RecordDriver/EDS.php @@ -29,6 +29,9 @@ namespace VuFind\RecordDriver; + +use Laminas\Cache\Storage\StorageInterface; + use function count; use function floatval; use function in_array; @@ -48,6 +51,8 @@ class EDS extends DefaultRecord { use Feature\IlsAwareTrait; + use \VuFind\Http\CachingDownloaderAwareTrait; + use \VuFind\Cache\CacheTrait; /** * Document types that are treated as ePub links. @@ -63,6 +68,35 @@ class EDS extends DefaultRecord */ protected $pdfTypes = ['ebook-pdf', 'pdflink']; + /** + * Constructor + * + * @param \VuFind\Config\Config $mainConfig VuFind main configuration (omit + * for built-in defaults) + * @param StorageInterface $cache Cache + */ + public function __construct( + $mainConfig = null, + StorageInterface $cache = null, + ) { + + $this->setCacheStorage($cache); + parent::__construct($mainConfig); + } + + + /** + * Set the key to store in the cache to share between EDS cover loader and record driver + * + * @param string $key Key to put in the cache + * + * @return string The determined key + */ + protected function getCacheKey($key = '') + { + return 'EDS_Shared_' . md5($key); + } + /** * Return the unique identifier of this record within EDS API; * As Accession Numbers (AN) could be repetitive, we use Database ID @@ -635,6 +669,7 @@ function ($data) { */ public function getThumbnail($size = 'small') { + $thumbnail = null; // Create a ranked list of sizes so we can use "best available" when appropriate. // Note that "thumb" is a value used by EBSCO, not by VuFind; it is included so // it can be matched up with requests for "small." @@ -647,7 +682,8 @@ public function getThumbnail($size = 'small') $target = $image['Target'] ?? ''; if ($target) { if ($currentFit === $desiredFit) { - return $target; + $thumbnail = $target; + break; } // Aim for the best match that is smaller than the requested size; we // don't want to overflow, but something small is better than nothing. @@ -658,10 +694,19 @@ public function getThumbnail($size = 'small') } } + if (!$thumbnail && $closestMatch) { + $thumbnail = $closestMatch; + } + // If EDS actually returned cover image data, use it. EDS only provides this data // for certain ebook packages. - if ($closestMatch) { - return $closestMatch; + if ($thumbnail) { + $this->putCachedData($this->getUniqueID(), $thumbnail); + return [ + 'recordid' => $this->getUniqueID(), + 'size' => $size, + 'source' => 'EDS' + ]; } // Optionally use VuFind's default cover loader diff --git a/module/VuFind/src/VuFind/RecordDriver/EDSFactory.php b/module/VuFind/src/VuFind/RecordDriver/EDSFactory.php new file mode 100644 index 000000000000..25efff2e7497 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/EDSFactory.php @@ -0,0 +1,73 @@ +. + * + * @category VuFind + * @package RecordDrivers + * @author Megan Schanz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ + +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for EDS RecordDriver. + * + * PHP version 8 + * + * @category VuFind + * @package RecordDrivers + * @author Megan Schanz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/vufind/ Main page + */ +class EDSFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException&\Throwable if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + ?array $options = null + ) { + $parts = explode('\\', $requestedName); + $configName = array_pop($parts); + $config = $container->get(\VuFind\Config\ConfigManagerInterface::class)->getConfigObject($configName); + $cache = $container->get(\VuFind\Cache\Manager::class)->getCache('object'); + return new $requestedName($config, $cache); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/PluginManager.php b/module/VuFind/src/VuFind/RecordDriver/PluginManager.php index 7110115caf77..cf08a8c057e9 100644 --- a/module/VuFind/src/VuFind/RecordDriver/PluginManager.php +++ b/module/VuFind/src/VuFind/RecordDriver/PluginManager.php @@ -91,7 +91,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - EDS::class => NameBasedConfigFactory::class, + EDS::class => EDSFactory::class, EIT::class => NameBasedConfigFactory::class, EPF::class => NameBasedConfigFactory::class, Pazpar2::class => NameBasedConfigFactory::class, From 1a02004b93eab9b0704a6e963d35d4e7c70a3237 Mon Sep 17 00:00:00 2001 From: Megan Schanz Date: Thu, 20 Aug 2026 16:31:40 -0400 Subject: [PATCH 2/6] Fix lint and test issues from previous commit --- .../VuFind/src/VuFind/Content/Covers/EDS.php | 4 ++-- .../src/VuFind/Content/Covers/EDSFactory.php | 2 +- module/VuFind/src/VuFind/RecordDriver/EDS.php | 9 +++----- .../src/VuFind/RecordDriver/EDSFactory.php | 2 +- .../src/VuFindTest/RecordDriver/EDSTest.php | 22 ++++++++++++------- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/module/VuFind/src/VuFind/Content/Covers/EDS.php b/module/VuFind/src/VuFind/Content/Covers/EDS.php index 01fd067b49c8..6b77eee65f61 100644 --- a/module/VuFind/src/VuFind/Content/Covers/EDS.php +++ b/module/VuFind/src/VuFind/Content/Covers/EDS.php @@ -49,7 +49,7 @@ class EDS extends \VuFind\Content\AbstractCover implements use \VuFind\Cache\CacheTrait; /** - * Constructor + * Constructor. * * @param StorageInterface $cache Cache */ @@ -60,7 +60,7 @@ public function __construct(StorageInterface $cache) } /** - * Set the key to store in the cache to share between EDS cover loader and record driver + * Set the key to store in the cache to share between EDS cover loader and record driver. * * @param string $key Key to put in the cache * diff --git a/module/VuFind/src/VuFind/Content/Covers/EDSFactory.php b/module/VuFind/src/VuFind/Content/Covers/EDSFactory.php index a7587f56a500..a80ce2f63088 100644 --- a/module/VuFind/src/VuFind/Content/Covers/EDSFactory.php +++ b/module/VuFind/src/VuFind/Content/Covers/EDSFactory.php @@ -46,7 +46,7 @@ class EDSFactory implements FactoryInterface { /** - * Create an object + * Create an object. * * @param ContainerInterface $container Service manager * @param string $requestedName Service being created diff --git a/module/VuFind/src/VuFind/RecordDriver/EDS.php b/module/VuFind/src/VuFind/RecordDriver/EDS.php index 6f0d0cc64bd7..f3322b1ad188 100644 --- a/module/VuFind/src/VuFind/RecordDriver/EDS.php +++ b/module/VuFind/src/VuFind/RecordDriver/EDS.php @@ -29,7 +29,6 @@ namespace VuFind\RecordDriver; - use Laminas\Cache\Storage\StorageInterface; use function count; @@ -69,7 +68,7 @@ class EDS extends DefaultRecord protected $pdfTypes = ['ebook-pdf', 'pdflink']; /** - * Constructor + * Constructor. * * @param \VuFind\Config\Config $mainConfig VuFind main configuration (omit * for built-in defaults) @@ -79,14 +78,12 @@ public function __construct( $mainConfig = null, StorageInterface $cache = null, ) { - $this->setCacheStorage($cache); parent::__construct($mainConfig); } - /** - * Set the key to store in the cache to share between EDS cover loader and record driver + * Set the key to store in the cache to share between EDS cover loader and record driver. * * @param string $key Key to put in the cache * @@ -705,7 +702,7 @@ public function getThumbnail($size = 'small') return [ 'recordid' => $this->getUniqueID(), 'size' => $size, - 'source' => 'EDS' + 'source' => 'EDS', ]; } diff --git a/module/VuFind/src/VuFind/RecordDriver/EDSFactory.php b/module/VuFind/src/VuFind/RecordDriver/EDSFactory.php index 25efff2e7497..d0f690ef45ad 100644 --- a/module/VuFind/src/VuFind/RecordDriver/EDSFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/EDSFactory.php @@ -46,7 +46,7 @@ class EDSFactory implements FactoryInterface { /** - * Create an object + * Create an object. * * @param ContainerInterface $container Service manager * @param string $requestedName Service being created diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/EDSTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/EDSTest.php index 70b479855b68..a548f6fe0a24 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/EDSTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/EDSTest.php @@ -31,6 +31,7 @@ namespace VuFindTest\RecordDriver; +use Laminas\Cache\Storage\StorageInterface; use VuFind\RecordDriver\EDS; use function array_slice; @@ -156,7 +157,8 @@ class EDSTest extends \PHPUnit\Framework\TestCase */ protected function getDriver(?string $test = null, ?array $config = null): EDS { - $record = new EDS(null, new \VuFind\Config\Config($config ?? $this->defaultDriverConfig)); + $cache = $this->createMock(StorageInterface::class); + $record = new EDS(new \VuFind\Config\Config($config ?? $this->defaultDriverConfig), $cache); if (null !== $test) { $json = $this->getJsonFixture('eds/' . $test . '.json'); $record->setRawData($json); @@ -612,24 +614,28 @@ public function testGetAllSubjectHeadingsFlattened(): void */ public static function getThumbnailProvider(): \Iterator { - yield 'thumb is upscaled to small' => ['small', 'small thumbnail link']; - yield 'medium is used as-is' => ['medium', 'medium thumbnail link']; - yield 'medium is upscaled to large' => ['large', 'medium thumbnail link']; + yield 'thumb is upscaled to small' => ['small']; + yield 'medium is used as-is' => ['medium']; + yield 'medium is upscaled to large' => ['large']; } /** * Test getThumbnail for a record. * - * @param string $size Size to request - * @param string $expected Expected result + * @param string $size Size to request * * @return void */ #[\PHPUnit\Framework\Attributes\DataProvider('getThumbnailProvider')] - public function testGetThumbnail(string $size, string $expected): void + public function testGetThumbnail(string $size): void { $driver = $this->getDriver('valid-eds-record'); - $this->assertEquals($expected, $driver->getThumbnail($size)); + $results = [ + 'recordid' => 'edsgob,edsgob.14707011', + 'size' => $size, + 'source' => 'EDS', + ]; + $this->assertEquals($results, $driver->getThumbnail($size)); } /** From ae7073ecc2cfa4a3b2e5a599ec9eab10df1e7317 Mon Sep 17 00:00:00 2001 From: Megan Schanz Date: Wed, 2 Sep 2026 09:48:10 -0400 Subject: [PATCH 3/6] Updates to EDS cover loader based on initial feedback --- config/vufind/EDS.ini | 6 ++++++ config/vufind/config.ini | 2 +- .../VuFind/src/VuFind/Content/Covers/EDS.php | 11 ++++------- .../VuFind/Content/Covers/vufind-exception.log | 0 module/VuFind/src/VuFind/RecordDriver/EDS.php | 18 ++++++++++++------ .../src/VuFind/RecordDriver/PluginManager.php | 2 +- 6 files changed, 24 insertions(+), 15 deletions(-) delete mode 100644 module/VuFind/src/VuFind/Content/Covers/vufind-exception.log diff --git a/config/vufind/EDS.ini b/config/vufind/EDS.ini index d15ec43dc47b..8a4f41c903e9 100644 --- a/config/vufind/EDS.ini +++ b/config/vufind/EDS.ini @@ -517,6 +517,12 @@ CatalogDatabaseId = "" ; CatalogANReplace[] = "-" [Cover] +; If you want the cover image URL to be used directly in page templates, set loadDirectly to +; true, but when set to false it will use the EDS cover loader to proxy the image URL +; through the application server providing additional privacy for end-users (since the image request +; will not be from the client IP). Note: when setting this to false, you must also update the +; coverimages setting in the config.ini to include EDS. +loadDirectly = false ; Normally EDS results display only EDS-supplied cover images, which are only available for ; some ebook data sources. Otherwise, EDS displays custom icons based on content type. ; Enable this setting to fallback to standard VuFind cover image loading behavior when no diff --git a/config/vufind/config.ini b/config/vufind/config.ini index 13520829e944..6b8efe65a95c 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -1137,7 +1137,7 @@ verify_server_certificate = false ; adjusted in the [Cache_OrbCover] section. ; - Summon service takes a Serials Solutions client key, NOT Summon API key! ; - EDS should be used when the EDS.ini is provided to use cover images from the EDS API -;coverimages = Syndetics:MySyndeticsId,LibraryThing:MyLibraryThingId,Google,ObalkyKnih,OpenLibrary,Summon:MySerialsSolutionsClientKey,Buchhandel,Contentcafe:MyContentCafeID,BrowZine,LocalFile:PathToFile,Koha,Orb +;coverimages = Syndetics:MySyndeticsId,LibraryThing:MyLibraryThingId,Google,ObalkyKnih,OpenLibrary,EDS,Summon:MySerialsSolutionsClientKey,Buchhandel,Contentcafe:MyContentCafeID,BrowZine,LocalFile:PathToFile,Koha,Orb ; When using the Koha cover provider, you should fill in this setting: ;koha_cover_url = "https://localhost/cgi-bin/koha/opac-image.pl" diff --git a/module/VuFind/src/VuFind/Content/Covers/EDS.php b/module/VuFind/src/VuFind/Content/Covers/EDS.php index 6b77eee65f61..ef9c84dcab27 100644 --- a/module/VuFind/src/VuFind/Content/Covers/EDS.php +++ b/module/VuFind/src/VuFind/Content/Covers/EDS.php @@ -40,12 +40,9 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class EDS extends \VuFind\Content\AbstractCover implements - \Psr\Log\LoggerAwareInterface, - \VuFind\Http\CachingDownloaderAwareInterface +class EDS extends \VuFind\Content\AbstractCover implements \Psr\Log\LoggerAwareInterface { use \VuFind\Log\LoggerAwareTrait; - use \VuFind\Http\CachingDownloaderAwareTrait; use \VuFind\Cache\CacheTrait; /** @@ -55,7 +52,7 @@ class EDS extends \VuFind\Content\AbstractCover implements */ public function __construct(StorageInterface $cache) { - $this->supportsRecordid = $this->cacheAllowed = true; + $this->supportsRecordid = true; $this->setCacheStorage($cache); } @@ -105,8 +102,8 @@ public function getUrl($key, $size, $ids) $recordId = $ids['recordid'] ?? ''; $url = $this->getCachedData($recordId); - if (str_starts_with($url, 'http://') || str_starts_with($url, 'https://')) { - $this->debug('Returning Cover image URL: ' . $url); + if (filter_var($url, FILTER_VALIDATE_URL)) { + $this->debug('Returning EDS Cover image URL: ' . $url); return $url; } diff --git a/module/VuFind/src/VuFind/Content/Covers/vufind-exception.log b/module/VuFind/src/VuFind/Content/Covers/vufind-exception.log deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/module/VuFind/src/VuFind/RecordDriver/EDS.php b/module/VuFind/src/VuFind/RecordDriver/EDS.php index 2b238505578a..d76644c41f4c 100644 --- a/module/VuFind/src/VuFind/RecordDriver/EDS.php +++ b/module/VuFind/src/VuFind/RecordDriver/EDS.php @@ -716,12 +716,18 @@ public function getThumbnail($size = 'small') // If EDS actually returned cover image data, use it. EDS only provides this data // for certain ebook packages. if ($thumbnail) { - $this->putCachedData($this->getUniqueID(), $thumbnail); - return [ - 'recordid' => $this->getUniqueID(), - 'size' => $size, - 'source' => 'EDS', - ]; + // Determine if we are using the cover loader method or direct load + $loadDirectly = $this->recordConfig?->Cover?->loadDirectly ?? true; + if ($loadDirectly) { + return $thumbnail; + } else { + $this->putCachedData($this->getUniqueID(), $thumbnail); + return [ + 'recordid' => $this->getUniqueID(), + 'size' => $size, + 'source' => 'EDS', + ]; + } } // Optionally use VuFind's default cover loader diff --git a/module/VuFind/src/VuFind/RecordDriver/PluginManager.php b/module/VuFind/src/VuFind/RecordDriver/PluginManager.php index cf08a8c057e9..9aa75f21b799 100644 --- a/module/VuFind/src/VuFind/RecordDriver/PluginManager.php +++ b/module/VuFind/src/VuFind/RecordDriver/PluginManager.php @@ -93,7 +93,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager protected $factories = [ EDS::class => EDSFactory::class, EIT::class => NameBasedConfigFactory::class, - EPF::class => NameBasedConfigFactory::class, + EPF::class => EDSFactory::class, Pazpar2::class => NameBasedConfigFactory::class, Primo::class => NameBasedConfigFactory::class, SolrAuthDefault::class => SolrDefaultWithoutSearchServiceFactory::class, From 6647e7a39a7af70f8ad9124283f96db15048eb24 Mon Sep 17 00:00:00 2001 From: Megan Schanz Date: Wed, 2 Sep 2026 09:50:21 -0400 Subject: [PATCH 4/6] Case correction to log message --- module/VuFind/src/VuFind/Content/Covers/EDS.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/VuFind/src/VuFind/Content/Covers/EDS.php b/module/VuFind/src/VuFind/Content/Covers/EDS.php index ef9c84dcab27..4288a5d30ed9 100644 --- a/module/VuFind/src/VuFind/Content/Covers/EDS.php +++ b/module/VuFind/src/VuFind/Content/Covers/EDS.php @@ -103,7 +103,7 @@ public function getUrl($key, $size, $ids) $url = $this->getCachedData($recordId); if (filter_var($url, FILTER_VALIDATE_URL)) { - $this->debug('Returning EDS Cover image URL: ' . $url); + $this->debug('Returning EDS cover image URL: ' . $url); return $url; } From 7dde2dd96c3cde2b05c8750e2c4c54ffefed6172 Mon Sep 17 00:00:00 2001 From: Megan Schanz Date: Wed, 2 Sep 2026 09:53:25 -0400 Subject: [PATCH 5/6] Update default of loadDirectly to true --- config/vufind/EDS.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/vufind/EDS.ini b/config/vufind/EDS.ini index 8a4f41c903e9..54e4e41ac0c7 100644 --- a/config/vufind/EDS.ini +++ b/config/vufind/EDS.ini @@ -522,7 +522,7 @@ CatalogDatabaseId = "" ; through the application server providing additional privacy for end-users (since the image request ; will not be from the client IP). Note: when setting this to false, you must also update the ; coverimages setting in the config.ini to include EDS. -loadDirectly = false +loadDirectly = true ; Normally EDS results display only EDS-supplied cover images, which are only available for ; some ebook data sources. Otherwise, EDS displays custom icons based on content type. ; Enable this setting to fallback to standard VuFind cover image loading behavior when no From 4d1e0c7ab13836677323d5fa64601668ff3c4cd9 Mon Sep 17 00:00:00 2001 From: Megan Schanz Date: Wed, 2 Sep 2026 10:12:27 -0400 Subject: [PATCH 6/6] Update test to check both scenarios of loadDirect in EDS Cover config --- .../src/VuFindTest/RecordDriver/EDSTest.php | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/EDSTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/EDSTest.php index 45b513fb508a..53296b5246f7 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/EDSTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/EDSTest.php @@ -63,6 +63,9 @@ class EDSTest extends \PHPUnit\Framework\TestCase 'default_sort' => 'relevance', ], 'ItemGlobalOrder' => [], + 'Cover' => [ + 'loadDirectly' => false, + ], ]; /** @@ -620,7 +623,19 @@ public static function getThumbnailProvider(): \Iterator } /** - * Test getThumbnail for a record. + * Data provider for testGetThumbnailDirect(). + * + * @return \Iterator + */ + public static function getThumbnailProviderDirect(): \Iterator + { + yield 'thumb is upscaled to small' => ['small', 'small thumbnail link']; + yield 'medium is used as-is' => ['medium', 'medium thumbnail link']; + yield 'medium is upscaled to large' => ['large', 'medium thumbnail link']; + } + + /** + * Test getThumbnail for a record using the proxy image URL loader. * * @param string $size Size to request * @@ -638,6 +653,21 @@ public function testGetThumbnail(string $size): void $this->assertEquals($results, $driver->getThumbnail($size)); } + /** + * Test getThumbnail for a record when using direct URL loader. + * + * @param string $size Size to request + * @param string $expected Expected result + * + * @return void + */ + #[\PHPUnit\Framework\Attributes\DataProvider('getThumbnailProviderDirect')] + public function testGetThumbnailDirect(string $size, string $expected): void + { + $driver = $this->getDriver('valid-eds-record', ['Cover' => ['loadDirectly' => true]]); + $this->assertEquals($expected, $driver->getThumbnail($size)); + } + /** * Test getThumbnail for a record that has no image data. *