Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.16% |
59 / 62 |
|
83.33% |
5 / 6 |
CRAP | |
0.00% |
0 / 1 |
Alma | |
95.16% |
59 / 62 |
|
83.33% |
5 / 6 |
22 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
fetchLinks | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
parseLinks | |
93.18% |
41 / 44 |
|
0.00% |
0 / 1 |
13.05 | |||
getKeyWithId | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
mapServiceType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
cleanupText | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | /** |
4 | * Alma Link Resolver Driver |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) The National Library of Finland 2019 |
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 Resolver_Drivers |
25 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki |
28 | */ |
29 | |
30 | namespace VuFind\Resolver\Driver; |
31 | |
32 | use function in_array; |
33 | |
34 | /** |
35 | * Alma Link Resolver Driver |
36 | * |
37 | * @category VuFind |
38 | * @package Resolver_Drivers |
39 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
40 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
41 | * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki |
42 | */ |
43 | class Alma extends AbstractBase |
44 | { |
45 | /** |
46 | * HTTP client |
47 | * |
48 | * @var \Laminas\Http\Client |
49 | */ |
50 | protected $httpClient; |
51 | |
52 | /** |
53 | * List of filter reasons that are ignored (displayed regardless of filtering) |
54 | * |
55 | * @var array |
56 | */ |
57 | protected $ignoredFilterReasons = ['Date Filter']; |
58 | |
59 | /** |
60 | * Constructor |
61 | * |
62 | * @param string $baseUrl Base URL for link resolver |
63 | * @param \Laminas\Http\Client $httpClient HTTP client |
64 | * @param array $options OpenURL Configuration (optional) |
65 | */ |
66 | public function __construct( |
67 | $baseUrl, |
68 | \Laminas\Http\Client $httpClient, |
69 | array $options = [] |
70 | ) { |
71 | parent::__construct($baseUrl); |
72 | $this->httpClient = $httpClient; |
73 | if (isset($options['ignoredFilterReasons'])) { |
74 | $this->ignoredFilterReasons |
75 | = empty($options['ignoredFilterReasons']) |
76 | ? [] : array_filter((array)$options['ignoredFilterReasons']); |
77 | } |
78 | } |
79 | |
80 | /** |
81 | * Fetch Links |
82 | * |
83 | * Fetches a set of links corresponding to an OpenURL |
84 | * |
85 | * @param string $openURL openURL (url-encoded) |
86 | * |
87 | * @return string Raw XML returned by resolver |
88 | */ |
89 | public function fetchLinks($openURL) |
90 | { |
91 | // Make the call to Alma and load results |
92 | $url = $this->getResolverUrl( |
93 | 'svc_dat=CTO&response_type=xml&' . $openURL |
94 | ); |
95 | return $this->httpClient->setUri($url)->send()->getBody(); |
96 | } |
97 | |
98 | /** |
99 | * Parse Links |
100 | * |
101 | * Parses an XML file returned by a link resolver |
102 | * and converts it to a standardised format for display |
103 | * |
104 | * @param string $xmlstr Raw XML returned by resolver |
105 | * |
106 | * @return array Array of values |
107 | */ |
108 | public function parseLinks($xmlstr) |
109 | { |
110 | $records = []; // array to return |
111 | try { |
112 | $xml = new \SimpleXmlElement($xmlstr); |
113 | } catch (\Exception $e) { |
114 | return $records; |
115 | } |
116 | |
117 | foreach ($xml->context_services->children() as $service) { |
118 | $filtered = $this->getKeyWithId($service, 'Filtered'); |
119 | if ('true' === $filtered) { |
120 | $reason = $this->getKeyWithId($service, 'Filter reason'); |
121 | if (!in_array($reason, $this->ignoredFilterReasons)) { |
122 | continue; |
123 | } |
124 | } |
125 | $originalServiceType = (string)$service->attributes()->service_type; |
126 | $serviceType = $this->mapServiceType($originalServiceType); |
127 | if (!$serviceType) { |
128 | continue; |
129 | } |
130 | if ('getWebService' === $serviceType) { |
131 | $title = $this->getKeyWithId($service, 'public_name'); |
132 | $href = $this->getKeyWithId($service, 'url'); |
133 | $access = ''; |
134 | } else { |
135 | $title = $this->getKeyWithId($service, 'package_display_name'); |
136 | if (!$title) { |
137 | $title = $this->getKeyWithId($service, 'package_public_name'); |
138 | } |
139 | $href = (string)$service->resolution_url; |
140 | if ( |
141 | 'getOpenAccessFullText' === $originalServiceType |
142 | || $this->getKeyWithId($service, 'Is_free') |
143 | ) { |
144 | $access = 'open'; |
145 | } else { |
146 | $access = 'limited'; |
147 | } |
148 | } |
149 | if ($coverage = $this->getKeyWithId($service, 'Availability')) { |
150 | $coverage = $this->cleanupText($coverage); |
151 | } |
152 | if ($notes = $this->getKeyWithId($service, 'public_note')) { |
153 | $notes = $this->cleanupText($notes); |
154 | } |
155 | $authentication = $this->getKeyWithId($service, 'Authentication_note'); |
156 | if ($authentication) { |
157 | $authentication = $this->cleanupText($authentication); |
158 | } |
159 | |
160 | $record = compact( |
161 | 'title', |
162 | 'coverage', |
163 | 'access', |
164 | 'href', |
165 | 'notes', |
166 | 'authentication' |
167 | ); |
168 | $record['service_type'] = $serviceType; |
169 | $records[] = $record; |
170 | } |
171 | return $records; |
172 | } |
173 | |
174 | /** |
175 | * Get a key with the specified id from the context_service element |
176 | * |
177 | * @param \SimpleXMLElement $service Service element |
178 | * @param string $id Key id |
179 | * |
180 | * @return string |
181 | */ |
182 | protected function getKeyWithId(\SimpleXMLElement $service, $id) |
183 | { |
184 | foreach ($service->keys->children() as $key) { |
185 | if ((string)$key->attributes()->id === $id) { |
186 | return (string)$key; |
187 | } |
188 | } |
189 | return ''; |
190 | } |
191 | |
192 | /** |
193 | * Map Alma service types to VuFind. Returns an empty string for an unmapped |
194 | * value. |
195 | * |
196 | * @param string $serviceType Alma service type |
197 | * |
198 | * @return string |
199 | */ |
200 | protected function mapServiceType($serviceType) |
201 | { |
202 | $map = [ |
203 | 'getFullTxt' => 'getFullTxt', |
204 | 'getOpenAccessFullText' => 'getFullTxt', |
205 | 'getHolding' => 'getHolding', |
206 | 'GeneralElectronicService' => 'getWebService', |
207 | 'DB' => 'getFullTxt', |
208 | 'Package' => 'getFullTxt', |
209 | ]; |
210 | return $map[$serviceType] ?? ''; |
211 | } |
212 | |
213 | /** |
214 | * Clean up textual information |
215 | * |
216 | * @param string $str Text |
217 | * |
218 | * @return string |
219 | */ |
220 | protected function cleanupText($str) |
221 | { |
222 | $str = trim(preg_replace('/<br\/?>/', ' ', $str)); |
223 | $str = strip_tags($str); |
224 | return $str; |
225 | } |
226 | } |