Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
99.28% |
416 / 419 |
|
93.18% |
41 / 44 |
CRAP | |
0.00% |
0 / 1 |
MultiBackend | |
99.28% |
416 / 419 |
|
93.18% |
41 / 44 |
152 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
init | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getStatus | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getStatuses | |
100.00% |
32 / 32 |
|
100.00% |
1 / 1 |
6 | |||
getHolding | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
4 | |||
getPurchaseHistory | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getLoginDrivers | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDefaultLoginDriver | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getNewItems | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
getDepartments | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getInstructors | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getCourses | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
findReserves | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
getMyProfile | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
getMyHolds | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
getMyStorageRetrievalRequests | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
3 | |||
checkRequestIsValid | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
4 | |||
checkStorageRetrievalRequestIsValid | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
getPickUpLocations | |
94.44% |
17 / 18 |
|
0.00% |
0 / 1 |
4.00 | |||
getDefaultPickUpLocation | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
getRequestGroups | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
4 | |||
getDefaultRequestGroup | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
5 | |||
placeHold | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
3 | |||
getCancelHoldDetails | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
1 | |||
placeStorageRetrievalRequest | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
4 | |||
checkILLRequestIsValid | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
1 | |||
getILLPickupLibraries | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
1 | |||
getILLPickupLocations | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
1 | |||
placeILLRequest | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
getMyILLRequests | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
3 | |||
getRequestBlocks | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
getAccountBlocks | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
getConfig | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
8 | |||
supportsMethod | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
7 | |||
__call | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getLocalId | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getSource | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getSourceForMethod | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getSourceFromParams | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
12 | |||
getDriver | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
addIdPrefixes | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
10.02 | |||
stripIdPrefixes | |
95.00% |
19 / 20 |
|
0.00% |
0 / 1 |
13 | |||
driverSupportsSource | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
callMethodIfSupported | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
7 |
1 | <?php |
2 | |
3 | /** |
4 | * Multiple Backend Driver. |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) The National Library of Finland 2012-2021. |
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 ILSdrivers |
25 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
26 | * @author Demian Katz <demian.katz@villanova.edu> |
27 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
28 | * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki |
29 | */ |
30 | |
31 | namespace VuFind\ILS\Driver; |
32 | |
33 | use VuFind\Exception\ILS as ILSException; |
34 | |
35 | use function call_user_func_array; |
36 | use function func_get_args; |
37 | use function in_array; |
38 | use function is_array; |
39 | use function is_callable; |
40 | use function is_int; |
41 | use function is_string; |
42 | use function strlen; |
43 | |
44 | /** |
45 | * Multiple Backend Driver. |
46 | * |
47 | * This driver allows to use multiple backends determined by a record id or |
48 | * user id prefix (e.g. source.12345). |
49 | * |
50 | * @category VuFind |
51 | * @package ILSdrivers |
52 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
53 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
54 | * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki |
55 | */ |
56 | class MultiBackend extends AbstractMultiDriver |
57 | { |
58 | use \VuFind\Log\LoggerAwareTrait { |
59 | logError as error; |
60 | } |
61 | |
62 | /** |
63 | * ID fields in holds |
64 | */ |
65 | public const HOLD_ID_FIELDS = ['id', 'item_id', 'cat_username']; |
66 | |
67 | /** |
68 | * The default driver to use |
69 | * |
70 | * @var string |
71 | */ |
72 | protected $defaultDriver; |
73 | |
74 | /** |
75 | * ILS authenticator |
76 | * |
77 | * @var \VuFind\Auth\ILSAuthenticator |
78 | */ |
79 | protected $ilsAuth; |
80 | |
81 | /** |
82 | * An array of methods that should determine source from a specific parameter |
83 | * field |
84 | * |
85 | * @var array |
86 | */ |
87 | protected $sourceCheckFields = [ |
88 | 'cancelHolds' => 'cat_username', |
89 | 'cancelILLRequests' => 'cat_username', |
90 | 'cancelStorageRetrievalRequests' => 'cat_username', |
91 | 'changePassword' => 'cat_username', |
92 | 'getCancelHoldDetails' => 'cat_username', |
93 | 'getCancelILLRequestDetails' => 'cat_username', |
94 | 'getCancelStorageRetrievalRequestDetails' => 'cat_username', |
95 | 'getMyFines' => 'cat_username', |
96 | 'getMyProfile' => 'cat_username', |
97 | 'getMyTransactionHistory' => 'cat_username', |
98 | 'getMyTransactions' => 'cat_username', |
99 | 'renewMyItems' => 'cat_username', |
100 | ]; |
101 | |
102 | /** |
103 | * Methods that don't have parameters that allow the correct source to be |
104 | * determined. These methods are only supported for the default driver. |
105 | */ |
106 | protected $methodsWithNoSourceSpecificParameters = [ |
107 | 'findReserves', |
108 | 'getCourses', |
109 | 'getDepartments', |
110 | 'getFunds', |
111 | 'getInstructors', |
112 | 'getNewItems', |
113 | 'getOfflineMode', |
114 | 'getSuppressedAuthorityRecords', |
115 | 'getSuppressedRecords', |
116 | 'loginIsHidden', |
117 | ]; |
118 | |
119 | /** |
120 | * Constructor |
121 | * |
122 | * @param \VuFind\Config\PluginManager $configLoader Configuration loader |
123 | * @param \VuFind\Auth\ILSAuthenticator $ilsAuth ILS authenticator |
124 | * @param PluginManager $dm ILS driver manager |
125 | */ |
126 | public function __construct( |
127 | \VuFind\Config\PluginManager $configLoader, |
128 | \VuFind\Auth\ILSAuthenticator $ilsAuth, |
129 | PluginManager $dm |
130 | ) { |
131 | parent::__construct($configLoader, $dm); |
132 | $this->ilsAuth = $ilsAuth; |
133 | } |
134 | |
135 | /** |
136 | * Initialize the driver. |
137 | * |
138 | * Validate configuration and perform all resource-intensive tasks needed to |
139 | * make the driver active. |
140 | * |
141 | * @throws ILSException |
142 | * @return void |
143 | */ |
144 | public function init() |
145 | { |
146 | parent::init(); |
147 | $this->defaultDriver = $this->config['General']['default_driver'] ?? null; |
148 | } |
149 | |
150 | /** |
151 | * Get Status |
152 | * |
153 | * This is responsible for retrieving the status information of a certain |
154 | * record. |
155 | * |
156 | * @param string $id The record id to retrieve the holdings for |
157 | * |
158 | * @throws ILSException |
159 | * @return mixed On success, an associative array with the following keys: |
160 | * id, availability (boolean), status, location, reserve, callnumber. |
161 | */ |
162 | public function getStatus($id) |
163 | { |
164 | $source = $this->getSource($id); |
165 | if ($driver = $this->getDriver($source)) { |
166 | $status = $driver->getStatus($this->getLocalId($id)); |
167 | return $this->addIdPrefixes($status, $source); |
168 | } |
169 | // Return an empty array if driver is not available; id can point to an ILS |
170 | // that's not currently configured. |
171 | return []; |
172 | } |
173 | |
174 | /** |
175 | * Get Statuses |
176 | * |
177 | * This is responsible for retrieving the status information for a |
178 | * collection of records. |
179 | * |
180 | * @param array $ids The array of record ids to retrieve the status for |
181 | * |
182 | * @throws ILSException |
183 | * @return array An array of getStatus() return values on success. |
184 | */ |
185 | public function getStatuses($ids) |
186 | { |
187 | // Group records by source and request statuses from the drivers |
188 | $grouped = []; |
189 | foreach ($ids as $id) { |
190 | $source = $this->getSource($id); |
191 | if (!isset($grouped[$source])) { |
192 | $driver = $this->getDriver($source); |
193 | $grouped[$source] = [ |
194 | 'driver' => $driver, |
195 | 'ids' => [], |
196 | ]; |
197 | } |
198 | $grouped[$source]['ids'][] = $id; |
199 | } |
200 | |
201 | // Process each group |
202 | $results = []; |
203 | foreach ($grouped as $source => $current) { |
204 | // Get statuses only if a driver is configured for this source |
205 | if ($current['driver']) { |
206 | $localIds = array_map( |
207 | function ($id) { |
208 | return $this->getLocalId($id); |
209 | }, |
210 | $current['ids'] |
211 | ); |
212 | try { |
213 | $statuses = $current['driver']->getStatuses($localIds); |
214 | } catch (ILSException $e) { |
215 | $statuses = array_map( |
216 | function ($id) { |
217 | return [ |
218 | ['id' => $id, 'error' => 'An error has occurred'], |
219 | ]; |
220 | }, |
221 | $localIds |
222 | ); |
223 | } |
224 | $statuses = array_map( |
225 | function ($status) use ($source) { |
226 | return $this->addIdPrefixes($status, $source); |
227 | }, |
228 | $statuses |
229 | ); |
230 | $results = array_merge($results, $statuses); |
231 | } |
232 | } |
233 | return $results; |
234 | } |
235 | |
236 | /** |
237 | * Get Holding |
238 | * |
239 | * This is responsible for retrieving the holding information of a certain |
240 | * record. |
241 | * |
242 | * @param string $id The record id to retrieve the holdings for |
243 | * @param array $patron Patron data |
244 | * @param array $options Extra options (not currently used) |
245 | * |
246 | * @return array On success, an associative array with the following |
247 | * keys: id, availability (boolean), status, location, reserve, callnumber, |
248 | * duedate, number, barcode. |
249 | * |
250 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
251 | */ |
252 | public function getHolding($id, array $patron = null, array $options = []) |
253 | { |
254 | $source = $this->getSource($id); |
255 | if ($driver = $this->getDriver($source)) { |
256 | // If the patron belongs to another source, just pass on an empty array |
257 | // to indicate that the patron has logged in but is not available for the |
258 | // current catalog. |
259 | if ( |
260 | $patron |
261 | && !$this->driverSupportsSource($source, $patron['cat_username']) |
262 | ) { |
263 | $patron = []; |
264 | } |
265 | $holdings = $driver->getHolding( |
266 | $this->getLocalId($id), |
267 | $this->stripIdPrefixes($patron, $source), |
268 | $options |
269 | ); |
270 | return $this->addIdPrefixes($holdings, $source); |
271 | } |
272 | // Return an empty array if driver is not available; id can point to an ILS |
273 | // that's not currently configured. |
274 | return []; |
275 | } |
276 | |
277 | /** |
278 | * Get Purchase History |
279 | * |
280 | * This is responsible for retrieving the acquisitions history data for the |
281 | * specific record (usually recently received issues of a serial). |
282 | * |
283 | * @param string $id The record id to retrieve the info for |
284 | * |
285 | * @throws ILSException |
286 | * @return array An array with the acquisitions data on success. |
287 | */ |
288 | public function getPurchaseHistory($id) |
289 | { |
290 | $source = $this->getSource($id); |
291 | if ($driver = $this->getDriver($source)) { |
292 | return $driver->getPurchaseHistory($this->getLocalId($id)); |
293 | } |
294 | // Return an empty array if driver is not available; id can point to an ILS |
295 | // that's not currently configured. |
296 | return []; |
297 | } |
298 | |
299 | /** |
300 | * Get available login targets (drivers enabled for login) |
301 | * |
302 | * @return string[] Source ID's |
303 | */ |
304 | public function getLoginDrivers() |
305 | { |
306 | return $this->config['Login']['drivers'] ?? []; |
307 | } |
308 | |
309 | /** |
310 | * Get default login driver |
311 | * |
312 | * @return string Default login driver or empty string |
313 | */ |
314 | public function getDefaultLoginDriver() |
315 | { |
316 | if (isset($this->config['Login']['default_driver'])) { |
317 | return $this->config['Login']['default_driver']; |
318 | } |
319 | $drivers = $this->getLoginDrivers(); |
320 | if ($drivers) { |
321 | return $drivers[0]; |
322 | } |
323 | return ''; |
324 | } |
325 | |
326 | /** |
327 | * Get New Items |
328 | * |
329 | * Retrieve the IDs of items recently added to the catalog. |
330 | * |
331 | * @param int $page Page number of results to retrieve (counting starts at 1) |
332 | * @param int $limit The size of each page of results to retrieve |
333 | * @param int $daysOld The maximum age of records to retrieve in days (max. 30) |
334 | * @param int $fundId optional fund ID to use for limiting results (use a value |
335 | * returned by getFunds, or exclude for no limit); note that "fund" may be a |
336 | * misnomer - if funds are not an appropriate way to limit your new item |
337 | * results, you can return a different set of values from getFunds. The |
338 | * important thing is that this parameter supports an ID returned by getFunds, |
339 | * whatever that may mean. |
340 | * |
341 | * @return array Associative array with 'count' and 'results' keys |
342 | */ |
343 | public function getNewItems($page, $limit, $daysOld, $fundId = null) |
344 | { |
345 | if ($driver = $this->getDriver($this->defaultDriver)) { |
346 | $result = $driver->getNewItems($page, $limit, $daysOld, $fundId); |
347 | if (isset($result['results'])) { |
348 | $result['results'] |
349 | = $this->addIdPrefixes($result['results'], $this->defaultDriver); |
350 | } |
351 | return $result; |
352 | } |
353 | throw new ILSException('No suitable backend driver found'); |
354 | } |
355 | |
356 | /** |
357 | * Get Departments |
358 | * |
359 | * Obtain a list of departments for use in limiting the reserves list. |
360 | * |
361 | * @return array An associative array with key = dept. ID, value = dept. name. |
362 | */ |
363 | public function getDepartments() |
364 | { |
365 | if ($driver = $this->getDriver($this->defaultDriver)) { |
366 | return $driver->getDepartments(); |
367 | } |
368 | throw new ILSException('No suitable backend driver found'); |
369 | } |
370 | |
371 | /** |
372 | * Get Instructors |
373 | * |
374 | * Obtain a list of instructors for use in limiting the reserves list. |
375 | * |
376 | * @return array An associative array with key = ID, value = name. |
377 | */ |
378 | public function getInstructors() |
379 | { |
380 | if ($driver = $this->getDriver($this->defaultDriver)) { |
381 | return $driver->getInstructors(); |
382 | } |
383 | throw new ILSException('No suitable backend driver found'); |
384 | } |
385 | |
386 | /** |
387 | * Get Courses |
388 | * |
389 | * Obtain a list of courses for use in limiting the reserves list. |
390 | * |
391 | * @return array An associative array with key = ID, value = name. |
392 | */ |
393 | public function getCourses() |
394 | { |
395 | if ($driver = $this->getDriver($this->defaultDriver)) { |
396 | return $driver->getCourses(); |
397 | } |
398 | throw new ILSException('No suitable backend driver found'); |
399 | } |
400 | |
401 | /** |
402 | * Find Reserves |
403 | * |
404 | * Obtain information on course reserves. |
405 | * |
406 | * @param string $course ID from getCourses (empty string to match all) |
407 | * @param string $inst ID from getInstructors (empty string to match all) |
408 | * @param string $dept ID from getDepartments (empty string to match all) |
409 | * |
410 | * @return mixed An array of associative arrays representing reserve items |
411 | */ |
412 | public function findReserves($course, $inst, $dept) |
413 | { |
414 | if ($driver = $this->getDriver($this->defaultDriver)) { |
415 | return $this->addIdPrefixes( |
416 | $driver->findReserves($course, $inst, $dept), |
417 | $this->defaultDriver, |
418 | ['BIB_ID'] |
419 | ); |
420 | } |
421 | throw new ILSException('No suitable backend driver found'); |
422 | } |
423 | |
424 | /** |
425 | * Get Patron Profile |
426 | * |
427 | * This is responsible for retrieving the profile for a specific patron. |
428 | * |
429 | * @param array $patron The patron array |
430 | * |
431 | * @return mixed Array of the patron's profile data |
432 | */ |
433 | public function getMyProfile($patron) |
434 | { |
435 | $source = $this->getSource($patron['cat_username']); |
436 | if ($driver = $this->getDriver($source)) { |
437 | return $this->addIdPrefixes( |
438 | $driver->getMyProfile($this->stripIdPrefixes($patron, $source)), |
439 | $source |
440 | ); |
441 | } |
442 | // Return an empty array if driver is not available; cat_username can point |
443 | // to an ILS that's not currently configured. |
444 | return []; |
445 | } |
446 | |
447 | /** |
448 | * Get Patron Holds |
449 | * |
450 | * This is responsible for retrieving all holds by a specific patron. |
451 | * |
452 | * @param array $patron The patron array from patronLogin |
453 | * |
454 | * @return mixed Array of the patron's holds |
455 | */ |
456 | public function getMyHolds($patron) |
457 | { |
458 | $source = $this->getSource($patron['cat_username']); |
459 | $holds = $this->callMethodIfSupported( |
460 | $source, |
461 | __FUNCTION__, |
462 | func_get_args(), |
463 | true, |
464 | false |
465 | ); |
466 | return $this->addIdPrefixes($holds, $source, self::HOLD_ID_FIELDS); |
467 | } |
468 | |
469 | /** |
470 | * Get Patron Call Slips |
471 | * |
472 | * This is responsible for retrieving all call slips by a specific patron. |
473 | * |
474 | * @param array $patron The patron array from patronLogin |
475 | * |
476 | * @return mixed Array of the patron's holds |
477 | */ |
478 | public function getMyStorageRetrievalRequests($patron) |
479 | { |
480 | $source = $this->getSource($patron['cat_username']); |
481 | if ($driver = $this->getDriver($source)) { |
482 | $params = [ |
483 | $this->stripIdPrefixes($patron, $source), |
484 | ]; |
485 | if (!$this->driverSupportsMethod($driver, __FUNCTION__, $params)) { |
486 | // Return empty array if not supported by the driver |
487 | return []; |
488 | } |
489 | $requests = $driver->getMyStorageRetrievalRequests(...$params); |
490 | return $this->addIdPrefixes($requests, $source); |
491 | } |
492 | throw new ILSException('No suitable backend driver found'); |
493 | } |
494 | |
495 | /** |
496 | * Check whether a hold or recall request is valid |
497 | * |
498 | * This is responsible for determining if an item is requestable |
499 | * |
500 | * @param string $id The Bib ID |
501 | * @param array $data An Array of item data |
502 | * @param array $patron An array of patron data |
503 | * |
504 | * @return mixed An array of data on the request including |
505 | * whether or not it is valid and a status message. Alternatively a boolean |
506 | * true if request is valid, false if not. |
507 | */ |
508 | public function checkRequestIsValid($id, $data, $patron) |
509 | { |
510 | if (!isset($patron['cat_username'])) { |
511 | return false; |
512 | } |
513 | $source = $this->getSource($patron['cat_username']); |
514 | if ($driver = $this->getDriver($source)) { |
515 | if (!$this->driverSupportsSource($source, $id)) { |
516 | return false; |
517 | } |
518 | return $driver->checkRequestIsValid( |
519 | $this->stripIdPrefixes($id, $source), |
520 | $this->stripIdPrefixes($data, $source), |
521 | $this->stripIdPrefixes($patron, $source) |
522 | ); |
523 | } |
524 | return false; |
525 | } |
526 | |
527 | /** |
528 | * Check whether a storage retrieval request is valid |
529 | * |
530 | * This is responsible for determining if an item is requestable |
531 | * |
532 | * @param string $id The Bib ID |
533 | * @param array $data An Array of item data |
534 | * @param array $patron An array of patron data |
535 | * |
536 | * @return mixed An array of data on the request including |
537 | * whether or not it is valid and a status message. Alternatively a boolean |
538 | * true if request is valid, false if not. |
539 | */ |
540 | public function checkStorageRetrievalRequestIsValid($id, $data, $patron) |
541 | { |
542 | $source = $this->getSource($patron['cat_username']); |
543 | if ($driver = $this->getDriver($source)) { |
544 | if ( |
545 | !$this->driverSupportsSource($source, $id) |
546 | || !is_callable([$driver, 'checkStorageRetrievalRequestIsValid']) |
547 | ) { |
548 | return false; |
549 | } |
550 | return $driver->checkStorageRetrievalRequestIsValid( |
551 | $this->stripIdPrefixes($id, $source), |
552 | $this->stripIdPrefixes($data, $source), |
553 | $this->stripIdPrefixes($patron, $source) |
554 | ); |
555 | } |
556 | return false; |
557 | } |
558 | |
559 | /** |
560 | * Get Pick Up Locations |
561 | * |
562 | * This is responsible get a list of valid library locations for holds / recall |
563 | * retrieval |
564 | * |
565 | * @param array $patron Patron information returned by the patronLogin |
566 | * method. |
567 | * @param array $holdDetails Optional array, only passed in when getting a list |
568 | * in the context of placing or editing a hold. When placing a hold, it contains |
569 | * most of the same values passed to placeHold, minus the patron data. When |
570 | * editing a hold it contains all the hold information returned by getMyHolds. |
571 | * May be used to limit the pickup options or may be ignored. The driver must |
572 | * not add new options to the return array based on this data or other areas of |
573 | * VuFind may behave incorrectly. |
574 | * |
575 | * @return array An array of associative arrays with locationID and |
576 | * locationDisplay keys |
577 | */ |
578 | public function getPickUpLocations($patron = false, $holdDetails = null) |
579 | { |
580 | $source = $this->getSource( |
581 | $patron['cat_username'] ?? $holdDetails['id'] ?? $holdDetails['item_id'] |
582 | ?? '' |
583 | ); |
584 | if ($driver = $this->getDriver($source)) { |
585 | if ($id = ($holdDetails['id'] ?? $holdDetails['item_id'] ?? '')) { |
586 | if (!$this->driverSupportsSource($source, $id)) { |
587 | // Return empty array since the sources don't match |
588 | return []; |
589 | } |
590 | } |
591 | $locations = $driver->getPickUpLocations( |
592 | $this->stripIdPrefixes($patron, $source), |
593 | $this->stripIdPrefixes( |
594 | $holdDetails, |
595 | $source, |
596 | self::HOLD_ID_FIELDS |
597 | ) |
598 | ); |
599 | return $this->addIdPrefixes($locations, $source); |
600 | } |
601 | throw new ILSException('No suitable backend driver found'); |
602 | } |
603 | |
604 | /** |
605 | * Get Default Pick Up Location |
606 | * |
607 | * Returns the default pick up location |
608 | * |
609 | * @param array $patron Patron information returned by the patronLogin |
610 | * method. |
611 | * @param array $holdDetails Optional array, only passed in when getting a list |
612 | * in the context of placing a hold; contains most of the same values passed to |
613 | * placeHold, minus the patron data. May be used to limit the pickup options |
614 | * or may be ignored. |
615 | * |
616 | * @return string A location ID |
617 | */ |
618 | public function getDefaultPickUpLocation($patron = false, $holdDetails = null) |
619 | { |
620 | $source = $this->getSource($patron['cat_username']); |
621 | if ($driver = $this->getDriver($source)) { |
622 | if ($holdDetails) { |
623 | if (!$this->driverSupportsSource($source, $holdDetails['id'])) { |
624 | // Return false since the sources don't match |
625 | return false; |
626 | } |
627 | } |
628 | $locations = $driver->getDefaultPickUpLocation( |
629 | $this->stripIdPrefixes($patron, $source), |
630 | $this->stripIdPrefixes($holdDetails, $source) |
631 | ); |
632 | return $this->addIdPrefixes($locations, $source); |
633 | } |
634 | throw new ILSException('No suitable backend driver found'); |
635 | } |
636 | |
637 | /** |
638 | * Get request groups |
639 | * |
640 | * @param int $id BIB ID |
641 | * @param array $patron Patron information returned by the patronLogin |
642 | * method. |
643 | * @param array $holdDetails Optional array, only passed in when getting a list |
644 | * in the context of placing a hold; contains most of the same values passed to |
645 | * placeHold, minus the patron data. May be used to limit the request group |
646 | * options or may be ignored. |
647 | * |
648 | * @return array An array of associative arrays with requestGroupId and |
649 | * name keys |
650 | */ |
651 | public function getRequestGroups($id, $patron, $holdDetails = null) |
652 | { |
653 | // Get source from patron as that will work also with the Demo driver: |
654 | $source = $this->getSource($patron['cat_username']); |
655 | if ($driver = $this->getDriver($source)) { |
656 | $params = [ |
657 | $this->stripIdPrefixes($id, $source), |
658 | $this->stripIdPrefixes($patron, $source), |
659 | $this->stripIdPrefixes($holdDetails, $source), |
660 | ]; |
661 | if ( |
662 | !$this->driverSupportsSource($source, $id) |
663 | || !$this->driverSupportsMethod($driver, __FUNCTION__, $params) |
664 | ) { |
665 | // Return empty array since the sources don't match or the method |
666 | // isn't supported by the driver |
667 | return []; |
668 | } |
669 | $groups = $driver->getRequestGroups(...$params); |
670 | return $groups; |
671 | } |
672 | throw new ILSException('No suitable backend driver found'); |
673 | } |
674 | |
675 | /** |
676 | * Get Default Request Group |
677 | * |
678 | * Returns the default request group |
679 | * |
680 | * @param array $patron Patron information returned by the patronLogin |
681 | * method. |
682 | * @param array $holdDetails Optional array, only passed in when getting a list |
683 | * in the context of placing a hold; contains most of the same values passed to |
684 | * placeHold, minus the patron data. May be used to limit the request group |
685 | * options or may be ignored. |
686 | * |
687 | * @return string A location ID |
688 | */ |
689 | public function getDefaultRequestGroup($patron, $holdDetails = null) |
690 | { |
691 | $source = $this->getSource($patron['cat_username']); |
692 | if ($driver = $this->getDriver($source)) { |
693 | $params = [ |
694 | $this->stripIdPrefixes($patron, $source), |
695 | $this->stripIdPrefixes($holdDetails, $source), |
696 | ]; |
697 | if (!empty($holdDetails)) { |
698 | if ( |
699 | !$this->driverSupportsSource($source, $holdDetails['id']) |
700 | || !$this->driverSupportsMethod($driver, __FUNCTION__, $params) |
701 | ) { |
702 | // Return false since the sources don't match or the method |
703 | // isn't supported by the driver |
704 | return false; |
705 | } |
706 | } |
707 | $locations = $driver->getDefaultRequestGroup(...$params); |
708 | return $this->addIdPrefixes($locations, $source); |
709 | } |
710 | throw new ILSException('No suitable backend driver found'); |
711 | } |
712 | |
713 | /** |
714 | * Place Hold |
715 | * |
716 | * Attempts to place a hold or recall on a particular item and returns |
717 | * an array with result details |
718 | * |
719 | * @param array $holdDetails An array of item and patron data |
720 | * |
721 | * @return mixed An array of data on the request including |
722 | * whether or not it was successful and a system message (if available) |
723 | */ |
724 | public function placeHold($holdDetails) |
725 | { |
726 | $source = $this->getSource($holdDetails['patron']['cat_username']); |
727 | if ($driver = $this->getDriver($source)) { |
728 | if (!$this->driverSupportsSource($source, $holdDetails['id'])) { |
729 | return [ |
730 | 'success' => false, |
731 | 'sysMessage' => 'ILSMessages::hold_wrong_user_institution', |
732 | ]; |
733 | } |
734 | $holdDetails = $this->stripIdPrefixes($holdDetails, $source); |
735 | return $driver->placeHold($holdDetails); |
736 | } |
737 | throw new ILSException('No suitable backend driver found'); |
738 | } |
739 | |
740 | /** |
741 | * Get Cancel Hold Details |
742 | * |
743 | * In order to cancel a hold, the ILS requires some information on the hold. |
744 | * This function returns the required information, which is then submitted |
745 | * as form data in Hold.php. This value is then extracted by the CancelHolds |
746 | * function. |
747 | * |
748 | * @param array $hold A single hold array from getMyHolds |
749 | * @param array $patron Patron information from patronLogin |
750 | * |
751 | * @return string Data for use in a form field |
752 | */ |
753 | public function getCancelHoldDetails($hold, $patron = []) |
754 | { |
755 | $source = $this->getSource( |
756 | $patron['cat_username'] ?? $hold['id'] ?? $hold['item_id'] ?? '' |
757 | ); |
758 | $params = [ |
759 | $this->stripIdPrefixes( |
760 | $hold, |
761 | $source, |
762 | self::HOLD_ID_FIELDS |
763 | ), |
764 | $this->stripIdPrefixes($patron, $source), |
765 | ]; |
766 | return $this->callMethodIfSupported($source, __FUNCTION__, $params, false); |
767 | } |
768 | |
769 | /** |
770 | * Place Storage Retrieval Request |
771 | * |
772 | * Attempts to place a storage retrieval request on a particular item and returns |
773 | * an array with result details |
774 | * |
775 | * @param array $details An array of item and patron data |
776 | * |
777 | * @return mixed An array of data on the request including |
778 | * whether or not it was successful and a system message (if available) |
779 | */ |
780 | public function placeStorageRetrievalRequest($details) |
781 | { |
782 | $source = $this->getSource($details['patron']['cat_username']); |
783 | $driver = $this->getDriver($source); |
784 | if ( |
785 | $driver |
786 | && is_callable([$driver, 'placeStorageRetrievalRequest']) |
787 | ) { |
788 | if (!$this->driverSupportsSource($source, $details['id'])) { |
789 | return [ |
790 | 'success' => false, |
791 | 'sysMessage' => 'ILSMessages::storage_wrong_user_institution', |
792 | ]; |
793 | } |
794 | return $driver->placeStorageRetrievalRequest( |
795 | $this->stripIdPrefixes($details, $source) |
796 | ); |
797 | } |
798 | throw new ILSException('No suitable backend driver found'); |
799 | } |
800 | |
801 | /** |
802 | * Check whether an ILL request is valid |
803 | * |
804 | * This is responsible for determining if an item is requestable |
805 | * |
806 | * @param string $id The Bib ID |
807 | * @param array $data An Array of item data |
808 | * @param array $patron An array of patron data |
809 | * |
810 | * @return mixed An array of data on the request including |
811 | * whether or not it is valid and a status message. Alternatively a boolean |
812 | * true if request is valid, false if not. |
813 | */ |
814 | public function checkILLRequestIsValid($id, $data, $patron) |
815 | { |
816 | $source = $this->getSource($id); |
817 | // Patron is not stripped so that the correct library can be determined |
818 | $params = [ |
819 | $this->stripIdPrefixes($id, $source), |
820 | $this->stripIdPrefixes($data, $source), |
821 | $patron, |
822 | ]; |
823 | return $this->callMethodIfSupported( |
824 | $source, |
825 | __FUNCTION__, |
826 | $params, |
827 | false, |
828 | false |
829 | ); |
830 | } |
831 | |
832 | /** |
833 | * Get ILL Pickup Libraries |
834 | * |
835 | * This is responsible for getting information on the possible pickup libraries |
836 | * |
837 | * @param string $id Record ID |
838 | * @param array $patron Patron |
839 | * |
840 | * @return bool|array False if request not allowed, or an array of associative |
841 | * arrays with libraries. |
842 | */ |
843 | public function getILLPickupLibraries($id, $patron) |
844 | { |
845 | $source = $this->getSource($id); |
846 | // Patron is not stripped so that the correct library can be determined |
847 | $params = [ |
848 | $this->stripIdPrefixes($id, $source, ['id']), |
849 | $patron, |
850 | ]; |
851 | return $this->callMethodIfSupported( |
852 | $source, |
853 | __FUNCTION__, |
854 | $params, |
855 | false, |
856 | false |
857 | ); |
858 | } |
859 | |
860 | /** |
861 | * Get ILL Pickup Locations |
862 | * |
863 | * This is responsible for getting a list of possible pickup locations for a |
864 | * library |
865 | * |
866 | * @param string $id Record ID |
867 | * @param string $pickupLib Pickup library ID |
868 | * @param array $patron Patron |
869 | * |
870 | * @return bool|array False if request not allowed, or an array of |
871 | * locations. |
872 | */ |
873 | public function getILLPickupLocations($id, $pickupLib, $patron) |
874 | { |
875 | $source = $this->getSource($id); |
876 | // Patron is not stripped so that the correct library can be determined |
877 | $params = [ |
878 | $this->stripIdPrefixes($id, $source, ['id']), |
879 | $pickupLib, |
880 | $patron, |
881 | ]; |
882 | return $this->callMethodIfSupported( |
883 | $source, |
884 | __FUNCTION__, |
885 | $params, |
886 | false, |
887 | false |
888 | ); |
889 | } |
890 | |
891 | /** |
892 | * Place ILL Request |
893 | * |
894 | * Attempts to place an ILL request on a particular item and returns |
895 | * an array with result details (or throws an exception on failure of support |
896 | * classes) |
897 | * |
898 | * @param array $details An array of item and patron data |
899 | * |
900 | * @return mixed An array of data on the request including |
901 | * whether or not it was successful and a system message (if available) |
902 | */ |
903 | public function placeILLRequest($details) |
904 | { |
905 | $source = $this->getSource($details['id']); |
906 | // Patron is not stripped so that the correct library can be determined |
907 | $params = [$this->stripIdPrefixes($details, $source, ['id'], ['patron'])]; |
908 | return $this->callMethodIfSupported( |
909 | $source, |
910 | __FUNCTION__, |
911 | $params, |
912 | false, |
913 | false |
914 | ); |
915 | } |
916 | |
917 | /** |
918 | * Get Patron ILL Requests |
919 | * |
920 | * This is responsible for retrieving all ILL Requests by a specific patron. |
921 | * |
922 | * @param array $patron The patron array from patronLogin |
923 | * |
924 | * @return mixed Array of the patron's ILL requests |
925 | */ |
926 | public function getMyILLRequests($patron) |
927 | { |
928 | $source = $this->getSource($patron['cat_username']); |
929 | if ($driver = $this->getDriver($source)) { |
930 | $params = [ |
931 | $this->stripIdPrefixes($patron, $source), |
932 | ]; |
933 | if (!$this->driverSupportsMethod($driver, __FUNCTION__, $params)) { |
934 | // Return empty array if not supported by the driver |
935 | return []; |
936 | } |
937 | $requests = $driver->getMyILLRequests(...$params); |
938 | return $this->addIdPrefixes( |
939 | $requests, |
940 | $source, |
941 | ['id', 'item_id', 'cat_username'] |
942 | ); |
943 | } |
944 | throw new ILSException('No suitable backend driver found'); |
945 | } |
946 | |
947 | /** |
948 | * Check whether the patron is blocked from placing requests (holds/ILL/SRR). |
949 | * |
950 | * @param array $patron Patron data from patronLogin(). |
951 | * |
952 | * @return mixed A boolean false if no blocks are in place and an array |
953 | * of block reasons if blocks are in place |
954 | */ |
955 | public function getRequestBlocks($patron) |
956 | { |
957 | $source = $this->getSource($patron['cat_username']); |
958 | if ($driver = $this->getDriver($source)) { |
959 | $params = [ |
960 | $this->stripIdPrefixes($patron, $source), |
961 | ]; |
962 | if (!$this->driverSupportsMethod($driver, __FUNCTION__, $params)) { |
963 | return false; |
964 | } |
965 | return $driver->getRequestBlocks(...$params); |
966 | } |
967 | throw new ILSException('No suitable backend driver found'); |
968 | } |
969 | |
970 | /** |
971 | * Check whether the patron has any blocks on their account. |
972 | * |
973 | * @param array $patron Patron data from patronLogin(). |
974 | * |
975 | * @return mixed A boolean false if no blocks are in place and an array |
976 | * of block reasons if blocks are in place |
977 | */ |
978 | public function getAccountBlocks($patron) |
979 | { |
980 | $source = $this->getSource($patron['cat_username']); |
981 | if ($driver = $this->getDriver($source)) { |
982 | $params = [ |
983 | $this->stripIdPrefixes($patron, $source), |
984 | ]; |
985 | if (!$this->driverSupportsMethod($driver, __FUNCTION__, $params)) { |
986 | return false; |
987 | } |
988 | return $driver->getAccountBlocks(...$params); |
989 | } |
990 | throw new ILSException('No suitable backend driver found'); |
991 | } |
992 | |
993 | /** |
994 | * Function which specifies renew, hold and cancel settings. |
995 | * |
996 | * @param string $function The name of the feature to be checked |
997 | * @param array $params Optional feature-specific parameters (array) |
998 | * |
999 | * @return array An array with key-value pairs. |
1000 | */ |
1001 | public function getConfig($function, $params = []) |
1002 | { |
1003 | $source = null; |
1004 | if (!empty($params)) { |
1005 | $source = $this->getSourceForMethod($function, $params); |
1006 | } |
1007 | if (!$source) { |
1008 | try { |
1009 | $patron = $this->ilsAuth->getStoredCatalogCredentials(); |
1010 | if ($patron && isset($patron['cat_username'])) { |
1011 | $source = $this->getSource($patron['cat_username']); |
1012 | } |
1013 | } catch (ILSException $e) { |
1014 | return []; |
1015 | } |
1016 | } |
1017 | |
1018 | $driver = $this->getDriver($source); |
1019 | |
1020 | // If we have resolved the needed driver, call getConfig and return. |
1021 | if ($driver && $this->driverSupportsMethod($driver, 'getConfig', $params)) { |
1022 | return $driver->getConfig( |
1023 | $function, |
1024 | $this->stripIdPrefixes($params, $source) |
1025 | ); |
1026 | } |
1027 | |
1028 | // If driver not available, return an empty array |
1029 | return []; |
1030 | } |
1031 | |
1032 | /** |
1033 | * Helper method to determine whether or not a certain method can be |
1034 | * called on this driver. Required method for any smart drivers. |
1035 | * |
1036 | * @param string $method The name of the called method. |
1037 | * @param array $params Array of passed parameters. |
1038 | * |
1039 | * @return bool True if the method can be called with the given parameters, |
1040 | * false otherwise. |
1041 | */ |
1042 | public function supportsMethod(string $method, array $params) |
1043 | { |
1044 | if ($method == 'getLoginDrivers' || $method == 'getDefaultLoginDriver') { |
1045 | return true; |
1046 | } |
1047 | |
1048 | $source = $this->getSourceForMethod($method, $params); |
1049 | if (!$source && $this->defaultDriver) { |
1050 | $source = $this->defaultDriver; |
1051 | } |
1052 | if (!$source) { |
1053 | // If we can't determine the source, assume we are capable of handling |
1054 | // the request unless the method is one that doesn't have parameters that |
1055 | // allow the correct source to be determined. |
1056 | return !in_array($method, $this->methodsWithNoSourceSpecificParameters); |
1057 | } |
1058 | |
1059 | $driver = $this->getDriver($source); |
1060 | return $driver && $this->driverSupportsMethod($driver, $method, $params); |
1061 | } |
1062 | |
1063 | /** |
1064 | * Default method -- pass along calls to the driver if a source can be determined |
1065 | * and a driver is available. Throws ILSException otherwise. |
1066 | * |
1067 | * @param string $methodName The name of the called method |
1068 | * @param array $params Array of passed parameters |
1069 | * |
1070 | * @throws ILSException |
1071 | * @return mixed Varies by method |
1072 | */ |
1073 | public function __call($methodName, $params) |
1074 | { |
1075 | return $this->callMethodIfSupported(null, $methodName, $params); |
1076 | } |
1077 | |
1078 | /** |
1079 | * Extract local ID from the given prefixed ID |
1080 | * |
1081 | * @param string $id The id to be split |
1082 | * |
1083 | * @return string Local ID |
1084 | */ |
1085 | protected function getLocalId($id) |
1086 | { |
1087 | $pos = strpos($id, '.'); |
1088 | if ($pos > 0) { |
1089 | return substr($id, $pos + 1); |
1090 | } |
1091 | $this->debug("Could not find local id in '$id'"); |
1092 | return $id; |
1093 | } |
1094 | |
1095 | /** |
1096 | * Extract source from the given ID |
1097 | * |
1098 | * @param string $id The id to be split |
1099 | * |
1100 | * @return string Source |
1101 | */ |
1102 | protected function getSource($id) |
1103 | { |
1104 | $pos = strpos($id, '.'); |
1105 | if ($pos > 0) { |
1106 | return substr($id, 0, $pos); |
1107 | } |
1108 | |
1109 | return ''; |
1110 | } |
1111 | |
1112 | /** |
1113 | * Get source for a method and parameters |
1114 | * |
1115 | * @param string $method Method |
1116 | * @param array $params Parameters |
1117 | * |
1118 | * @return string |
1119 | */ |
1120 | protected function getSourceForMethod(string $method, array $params): string |
1121 | { |
1122 | $source = ''; |
1123 | $checkFields = $this->sourceCheckFields[$method] ?? null; |
1124 | if ($checkFields) { |
1125 | $source = $this->getSourceFromParams($params, (array)$checkFields); |
1126 | } else { |
1127 | $source = $this->getSourceFromParams($params); |
1128 | } |
1129 | return $source; |
1130 | } |
1131 | |
1132 | /** |
1133 | * Get source from method parameters |
1134 | * |
1135 | * @param array $params Parameters of a driver method call |
1136 | * @param array $allowedKeys Keys to use for source identification |
1137 | * |
1138 | * @return string Source id or empty string if not found |
1139 | */ |
1140 | protected function getSourceFromParams( |
1141 | $params, |
1142 | $allowedKeys = [0, 'id', 'cat_username'] |
1143 | ) { |
1144 | if (!is_array($params)) { |
1145 | if (is_string($params)) { |
1146 | $source = $this->getSource($params); |
1147 | if ($source && isset($this->drivers[$source])) { |
1148 | return $source; |
1149 | } |
1150 | } |
1151 | return ''; |
1152 | } |
1153 | foreach ($params as $key => $value) { |
1154 | $source = false; |
1155 | if (is_array($value) && (is_int($key) || $key === 'patron')) { |
1156 | $source = $this->getSourceFromParams($value, $allowedKeys); |
1157 | } elseif (in_array($key, $allowedKeys)) { |
1158 | $source = $this->getSource($value); |
1159 | } |
1160 | if ($source && isset($this->drivers[$source])) { |
1161 | return $source; |
1162 | } |
1163 | } |
1164 | return ''; |
1165 | } |
1166 | |
1167 | /** |
1168 | * Find the correct driver for the correct configuration file for the |
1169 | * given source and cache an initialized copy of it. |
1170 | * |
1171 | * @param string $source The source name of the driver to get. |
1172 | * |
1173 | * @return mixed On success a driver object, otherwise null. |
1174 | */ |
1175 | protected function getDriver($source) |
1176 | { |
1177 | if (!$source) { |
1178 | // Check for default driver |
1179 | if ($this->defaultDriver) { |
1180 | $this->debug('Using default driver ' . $this->defaultDriver); |
1181 | $source = $this->defaultDriver; |
1182 | } |
1183 | } |
1184 | return parent::getDriver($source); |
1185 | } |
1186 | |
1187 | /** |
1188 | * Change local ID's to global ID's in the given array |
1189 | * |
1190 | * @param mixed $data The data to be modified, normally |
1191 | * array or array of arrays |
1192 | * @param string $source Source code |
1193 | * @param array $modifyFields Fields to be modified in the array |
1194 | * |
1195 | * @return mixed Modified array or empty/null if that input was |
1196 | * empty/null |
1197 | */ |
1198 | protected function addIdPrefixes( |
1199 | $data, |
1200 | $source, |
1201 | $modifyFields = ['id', 'cat_username'] |
1202 | ) { |
1203 | if (empty($source) || empty($data) || !is_array($data)) { |
1204 | return $data; |
1205 | } |
1206 | |
1207 | foreach ($data as $key => $value) { |
1208 | if (null === $value) { |
1209 | continue; |
1210 | } |
1211 | if (is_array($value)) { |
1212 | $data[$key] = $this->addIdPrefixes( |
1213 | $value, |
1214 | $source, |
1215 | $modifyFields |
1216 | ); |
1217 | } else { |
1218 | if ( |
1219 | !ctype_digit((string)$key) |
1220 | && $value !== '' |
1221 | && in_array($key, $modifyFields) |
1222 | ) { |
1223 | $data[$key] = "$source.$value"; |
1224 | } |
1225 | } |
1226 | } |
1227 | return $data; |
1228 | } |
1229 | |
1230 | /** |
1231 | * Change global ID's to local ID's in the given array |
1232 | * |
1233 | * @param mixed $data The data to be modified, normally |
1234 | * array or array of arrays |
1235 | * @param string $source Source code |
1236 | * @param array $modifyFields Fields to be modified in the array |
1237 | * @param array $ignoreFields Fields to be ignored during recursive processing |
1238 | * |
1239 | * @return mixed Modified array or empty/null if that input was |
1240 | * empty/null |
1241 | */ |
1242 | protected function stripIdPrefixes( |
1243 | $data, |
1244 | $source, |
1245 | $modifyFields = ['id', 'cat_username'], |
1246 | $ignoreFields = [] |
1247 | ) { |
1248 | if (!isset($data) || empty($data)) { |
1249 | return $data; |
1250 | } |
1251 | $array = is_array($data) ? $data : [$data]; |
1252 | |
1253 | foreach ($array as $key => $value) { |
1254 | if (null === $value) { |
1255 | continue; |
1256 | } |
1257 | if (is_array($value)) { |
1258 | if (in_array($key, $ignoreFields)) { |
1259 | continue; |
1260 | } |
1261 | $array[$key] = $this->stripIdPrefixes( |
1262 | $value, |
1263 | $source, |
1264 | $modifyFields |
1265 | ); |
1266 | } else { |
1267 | $prefixLen = strlen($source) + 1; |
1268 | if ( |
1269 | (!is_array($data) |
1270 | || (!ctype_digit((string)$key) && in_array($key, $modifyFields))) |
1271 | && strncmp("$source.", $value, $prefixLen) == 0 |
1272 | ) { |
1273 | $array[$key] = substr($value, $prefixLen); |
1274 | } |
1275 | } |
1276 | } |
1277 | return is_array($data) ? $array : $array[0]; |
1278 | } |
1279 | |
1280 | /** |
1281 | * Check if the given ILS driver supports the source of a record |
1282 | * |
1283 | * @param string $driverSource Driver's source identifier |
1284 | * @param string $id Prefixed identifier to compare with |
1285 | * |
1286 | * @return bool |
1287 | */ |
1288 | protected function driverSupportsSource(string $driverSource, string $id): bool |
1289 | { |
1290 | // Same source is always ok: |
1291 | if ($this->getSource($id) === $driverSource) { |
1292 | return true; |
1293 | } |
1294 | // Demo driver supports any record source: |
1295 | $driver = $this->getDriver($driverSource); |
1296 | return $driver instanceof \VuFind\ILS\Driver\Demo; |
1297 | } |
1298 | |
1299 | /** |
1300 | * Check that the requested method is supported and call it. |
1301 | * |
1302 | * @param string $source Source ID or null to determine from parameters |
1303 | * @param string $method Method name |
1304 | * @param array $params Method parameters |
1305 | * @param bool $stripPrefixes Whether to strip ID prefixes from all input |
1306 | * parameters |
1307 | * @param bool $addPrefixes Whether to add ID prefixes to the call result |
1308 | * |
1309 | * @return mixed |
1310 | * @throws ILSException |
1311 | */ |
1312 | protected function callMethodIfSupported( |
1313 | ?string $source, |
1314 | string $method, |
1315 | array $params, |
1316 | bool $stripPrefixes = true, |
1317 | bool $addPrefixes = true |
1318 | ) { |
1319 | if (null === $source) { |
1320 | $source = $this->getSourceForMethod($method, $params); |
1321 | } |
1322 | $driver = $this->getDriver($source); |
1323 | if ($driver) { |
1324 | if ($stripPrefixes) { |
1325 | foreach ($params as &$param) { |
1326 | $param = $this->stripIdPrefixes($param, $source); |
1327 | } |
1328 | unset($param); |
1329 | } |
1330 | if ($this->driverSupportsMethod($driver, $method, $params)) { |
1331 | $result = call_user_func_array([$driver, $method], $params); |
1332 | if ($addPrefixes) { |
1333 | $result = $this->addIdPrefixes($result, $source); |
1334 | } |
1335 | return $result; |
1336 | } |
1337 | } |
1338 | throw new ILSException('No suitable backend driver found'); |
1339 | } |
1340 | } |