<?php /** * Copyright (C) Leipzig University Library 2019. * * PHP Version 7 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind * @package Resolver * @author Gregor Gawol <gawol@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2 * @link https://vufind.org/wiki/development Wiki */ namespace fid\VuFind\Resolver\Driver; use DOMDocument; use DOMXpath; use fid\Service\Client; use finc\Resolver\Driver\Ezb as BaseEzb; use VuFind\ILS\Connection; use Zend\Config\Config; /** * Class Ezb * Driver for EZB link resolver * * @category VuFind * @package Resolver * @author Gregor Gawol <gawol@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2 * @link https://vufind.org/wiki/development Wiki */ class Ezb extends BaseEzb { /** * Link resolver configuration * * @var Config */ protected $config; /** * ILS connection * * @var Connection */ protected $ils; /** * FID API Client * * @var \fid\Service\Client */ protected $fidClient; /** * Base URL EZB Resolver * * @var string */ protected $baseUrl; /** * Setter for $config * * @param Config $config resolver configuration * * @return void */ public function setConfig(Config $config): void { $this->config = $config; } /** * Setter for $fidClient * * @param Client $fidClient fid client * * @return void */ public function setFidClient(Client $fidClient): void { $this->fidClient = $fidClient; } /** * Setter for $ils * * @param Connection $ils ILS connection * * @return void */ public function setIls(Connection $ils): void { $this->ils = $ils; } /** * Setter for $baseUrl * * @param string $baseUrl resolver's base URL * * @return void */ public function setBaseUrl(string $baseUrl): void { $this->baseUrl = $baseUrl; } /** * Parse Links * * Parses an XML file returned by a link resolver * and converts it to a standardised format for display * * @param string $xmlstr Raw XML returned by resolver * * @return array Array of values */ public function parseLinks($xmlstr) { $records = []; // array to return $xml = new DOMDocument(); if (!@$xml->loadXML($xmlstr)) { return $records; } $xpath = new DOMXpath($xml); // get results for online $this->getElectronicResults('0', 'Free', $records, $xpath); $this->getElectronicResults('1', 'Partially free', $records, $xpath); $this->getElectronicResults('2', 'Licensed', $records, $xpath); $this->getElectronicResults('3', 'Partially licensed', $records, $xpath); $this->getElectronicResults('4', 'Not free', $records, $xpath); $this->getElectronicResults('10', 'Not free', $records, $xpath); $this->getElectronicResults('-1', 'Not free', $records, $xpath); // get results for print, only if available $this->getPrintResults('2', 'Print available', $records, $xpath); $this->getPrintResults('3', 'Print partially available', $records, $xpath); $records['messages'] = $this->messages; $records['stateOfResults'] = $this->stateOfResults; return $records; } /** * Get Resolver Url * * Transform the OpenURL as needed to get a working link to the resolver. * * Extends VuFind EZB Resolver * * @param string $openURL openURL (url-encoded) * * @return string Returns resolver specific url */ public function getResolverUrl($openURL) { // Unfortunately the EZB-API only allows OpenURL V0.1 and // breaks when sending a non expected parameter (like an ISBN). // So we do have to 'downgrade' the OpenURL-String from V1.0 to V0.1 // and exclude all parameters that are not compliant with the EZB. // Parse OpenURL into associative array: $tmp = explode('&', $openURL); $parsed = []; foreach ($tmp as $current) { $tmp2 = explode('=', $current, 2); $parsed[$tmp2[0]] = $tmp2[1]; } // Downgrade 1.0 to 0.1 if ($parsed['ctx_ver'] == 'Z39.88-2004') { $openURL = $this->downgradeOpenUrl($parsed); } // use ils for dynamic resolver data // fid specific adaption if (isset($this->config->useILS) && $this->config->useILS) { if ($this->fidClient->isLoggedOn()) { $user = $this->fidClient->requestUserDetails(); $libraries = $this->fidClient->requestLibraryList(); $homeLibrary = $user->getHomeLibrary(); // Obtain user information from ILS: $libInfo = $libraries[$homeLibrary] ?? $libraries['AAAAA']; $bibId = $libInfo->getEzb(); $isil = $libInfo->getIsil(); } else { // standard bibid in EZB resolver $bibId = 'AAAAA'; $isil = ''; } $openURL .= '&pid=' . 'bibid%3D' . $bibId . (!empty($isil) ? '%26isil%3D' . $isil : ''); } elseif (isset($this->config->bibid)) { $openURL .= '&pid=' . 'bibid%3D' . $this->config->bibid; } elseif (isset($this->config->useClientIp) && $this->config->useClientIp ) { // make the request IP-based to allow automatic // indication on institution level $openURL .= '&pid=client_ip%3D' . $_SERVER['REMOTE_ADDR']; } // fid specific adaption - END $openURL .= isset($parsed['zdbid']) ? '%26zdbid%3D' . $parsed['zdbid'] : ''; // Make the call to the EZB and load results $url = $this->baseUrl . '?' . $openURL; return $url; } /** * Extract electronic results from the EZB response and inject them into the * $records array. * * @param string $state The state attribute value to extract * @param string $coverage The coverage string to associate with the state * @param array $records The array of results to update * @param DOMXpath $xpath The XPath object containing parsed XML * * @return void */ protected function getElectronicResults($state, $coverage, &$records, $xpath) { $results = $xpath->query( "/OpenURLResponseXML/Full/ElectronicData/ResultList/Result[@state=" . $state . "]" ); $accessLevel_mapping = [ 'article' => 'article', 'issue' => 'issue', 'volume' => 'default', 'year' => 'default', 'homepage' => 'default', 'abstract' => 'abstract' ]; $i = 0; foreach ($results as $result) { $record = []; $accessLevelXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" . "Result[@state={$state}][".($i+1)."]/AccessLevel"; $accessLevel = $xpath->query($accessLevelXP, $result)->item(0); if (isset($accessLevel)) { $record['title'] = $accessLevel_mapping[strip_tags($accessLevel->nodeValue)]; } $accessUrlXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" . "Result[@state={$state}][".($i+1)."]/AccessURL"; $accessUrl = $xpath->query($accessUrlXP, $result)->item(0); $journalUrlXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" . "Result[@state={$state}][".($i+1)."]/JournalURL"; $journalUrl = $xpath->query($journalUrlXP, $result)->item(0); if (isset($accessUrl->nodeValue)) { $record['href'] = $accessUrl->nodeValue; } elseif (isset($journalUrl)) { $record['href'] = $journalUrl->nodeValue; } $stateOfResultXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" . "Result/@state"; $stateOfResult = $xpath->query($stateOfResultXP, $result)->item(0); if (isset($stateOfResult->nodeValue)) { $this->stateOfResults[] = $stateOfResult->nodeValue; } $record['access'] = $this->electronic_state_access_mapping[$state]; // Service type needs to be hard-coded for calling code to properly // categorize links. The commented code below picks a more appropriate // value but won't work for now -- retained for future reference. //$service_typeXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" // . "Result[@state={$state}][".($i+1)."]/AccessLevel"; //$record['service_type'] // = $xpath->query($service_typeXP, $result)->item(0)->nodeValue; $record['service_type'] = 'getFullTxt'; array_push($records, $record); $i++; } } /** * Extract print results from the EZB response and inject them into the * $records array. * * @param string $state The state attribute value to extract * @param string $coverage The coverage string to associate with the state * @param array $records The array of results to update * @param DOMXpath $xpath The XPath object containing parsed XML * * @return void */ protected function getPrintResults($state, $coverage, &$records, $xpath) { $results = $xpath->query( "/OpenURLResponseXML/Full/PrintData/ResultList/Result[@state={$state}]" ); /* override state 3 from 'limited' to 'open' */ $this->print_state_access_mapping['3'] = 'open'; $i = 0; foreach ($results as $result) { $record = []; $resultXP = "/OpenURLResponseXML/Full/PrintData/ResultList/" . "Result[@state={$state}][" . ($i + 1) . "]"; $resultElements = [ 'Title', 'Location', 'Signature', 'Period', 'Holding_comment' ]; // store data in separate array field fid-specific foreach ($resultElements as $element) { $elem = $xpath->query($resultXP . "/" . $element, $result)->item(0); if (isset($elem->nodeValue)) { $record[$element] = strip_tags($elem->nodeValue); } } /* messages depends on access state and will be build within templates */ $record['access'] = $this->print_state_access_mapping[$state]; // Service type needs to be hard-coded for calling code to properly // categorize links. The commented code below picks a more appropriate // value but won't work for now -- retained for future reference. //$service_typeXP = "/OpenURLResponseXML/Full/PrintData/References" // . "/Reference/Label"; //$record['service_type'] // = $xpath->query($service_typeXP, $result)->item($i)->nodeValue; $record['service_type'] = 'getHolding'; array_push($records, $record); $i++; } } }