Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.53% covered (success)
91.53%
54 / 59
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Syndetics
91.53% covered (success)
91.53%
54 / 59
57.14% covered (warning)
57.14%
4 / 7
23.32
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getUrl
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
6.01
 getBaseUrl
81.25% covered (warning)
81.25%
13 / 16
0.00% covered (danger)
0.00%
0 / 1
8.42
 getImageFilenameFromSize
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getMetadataXML
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getImageFilenameFromMetadata
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
 getImageUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * Syndetics cover content loader.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2010.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Content
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/development Wiki
28 */
29
30namespace VuFind\Content\Covers;
31
32use DOMDocument;
33
34/**
35 * Syndetics cover content loader.
36 *
37 * @category VuFind
38 * @package  Content
39 * @author   Demian Katz <demian.katz@villanova.edu>
40 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
41 * @link     https://vufind.org/wiki/development Wiki
42 */
43class Syndetics extends \VuFind\Content\AbstractCover implements \VuFind\Http\CachingDownloaderAwareInterface
44{
45    use \VuFind\Http\CachingDownloaderAwareTrait;
46
47    /**
48     * Use SSL URLs?
49     *
50     * @var bool
51     */
52    protected $useSSL;
53
54    /**
55     * Use Syndetics image fallback ?
56     *
57     * @var bool
58     */
59    protected $useSyndeticsCoverImageFallback;
60
61    /**
62     * Constructor
63     *
64     * @param ?\Laminas\Config\Config $config Syndetics configuration
65     */
66    public function __construct(?\Laminas\Config\Config $config = null)
67    {
68        $this->useSSL = $config->use_ssl ?? false;
69        $this->useSyndeticsCoverImageFallback = $config->use_syndetics_cover_image_fallback ?? false;
70        $this->supportsIsbn = $this->supportsIssn = $this->supportsOclc
71            = $this->supportsUpc = $this->cacheAllowed = true;
72    }
73
74    /**
75     * Get image URL for a particular API key and set of IDs (or false if invalid).
76     *
77     * @param string $key  API key
78     * @param string $size Size of image to load (small/medium/large)
79     * @param array  $ids  Associative array of identifiers (keys may include 'isbn'
80     * pointing to an ISBN object and 'issn' pointing to a string)
81     *
82     * @return string|bool
83     */
84    public function getUrl($key, $size, $ids)
85    {
86        $baseUrl = $this->getBaseUrl($key, $ids);
87        if ($baseUrl == false) {
88            return false;
89        }
90        if ($this->useSyndeticsCoverImageFallback) {
91            $filename = $this->getImageFilenameFromSize($size);
92            if ($filename == false) {
93                return false;
94            }
95        } else {
96            $xmldoc = $this->getMetadataXML($baseUrl);
97            if ($xmldoc == false) {
98                return false;
99            }
100            $filename = $this->getImageFilenameFromMetadata($xmldoc, $size);
101            if ($filename == false) {
102                return false;
103            }
104        }
105        return $this->getImageUrl($baseUrl, $filename);
106    }
107
108    /**
109     * Return the base Syndetics URL for both the metadata and image URLs.
110     *
111     * @param string $key API key
112     * @param array  $ids Associative array of identifiers (keys may include 'isbn'
113     * pointing to an ISBN object and 'issn' pointing to a string)
114     *
115     * @return string|bool Base URL, or false if no identifier can be used
116     */
117    protected function getBaseUrl($key, $ids)
118    {
119        $url = $this->useSSL
120            ? 'https://secure.syndetics.com' : 'http://syndetics.com';
121        $url .= "/index.aspx?client={$key}";
122        $ident = '';
123        if (isset($ids['isbn']) && $ids['isbn']->isValid()) {
124            $isbn = $ids['isbn']->get13();
125            $ident .= "&isbn={$isbn}";
126        }
127        if (isset($ids['issn'])) {
128            $ident .= "&issn={$ids['issn']}";
129        }
130        if (isset($ids['oclc'])) {
131            $ident .= "&oclc={$ids['oclc']}";
132        }
133        if (isset($ids['upc'])) {
134            $ident .= "&upc={$ids['upc']}";
135        }
136        if (empty($ident)) {
137            return false;
138        }
139        return $url . $ident;
140    }
141
142    /**
143     * Calculate the image filename based on the size, without checking if it exists in the metadata.
144     *
145     * @param string $size Size of image to load (small/medium/large)
146     *
147     * @return string|bool Image filename, or false if the size is not 'small', 'medium' or 'large'
148     */
149    protected function getImageFilenameFromSize($size)
150    {
151        return match ($size) {
152            'small' => 'SC.GIF',
153            'medium' => 'MC.GIF',
154            'large' => 'LC.JPG',
155            default => false,
156        };
157    }
158
159    /**
160     * Get the Syndetics metadata as XML, using a cache.
161     *
162     * @param $baseUrl string  Base URL for the Syndetics query
163     *
164     * @return DOMDocument|bool The metadata as a DOM XML document, or false if the document cannot be parsed.
165     */
166    protected function getMetadataXML($baseUrl)
167    {
168        $url = $baseUrl . '/index.xml';
169        if (!isset($this->cachingDownloader)) {
170            throw new \Exception('CachingDownloader initialization failed.');
171        }
172        $body = $this->cachingDownloader->download($url);
173        $dom = new DOMDocument();
174        return $dom->loadXML($body) ? $dom : false;
175    }
176
177    /**
178     * Find the image filename in the XML returned from API.
179     *
180     * @param DOMDocument $xmldoc Parsed XML document
181     * @param string      $size   Size of image to load (small/medium/large)
182     *
183     * @return string|bool Image filename, or false if none matches
184     */
185    protected function getImageFilenameFromMetadata($xmldoc, $size)
186    {
187        $elementName = match ($size) {
188            'small' => 'SC',
189            'medium' => 'MC',
190            'large' => 'LC',
191            default => false,
192        };
193        if ($elementName == false) {
194            return false;
195        }
196        $nodes = $xmldoc->getElementsByTagName($elementName);
197        if ($nodes->length == 0) {
198            return false;
199        }
200        return $nodes->item(0)->nodeValue;
201    }
202
203    /**
204     * Return the full image url.
205     *
206     * @param $baseUrl  string  Base URL for the Syndetics query
207     * @param $filename string  Image filename
208     *
209     * @return string Full url of the image
210     */
211    protected function getImageUrl($baseUrl, $filename)
212    {
213        return $baseUrl . "/{$filename}";
214    }
215}