From 20ace5a805e5c71be52f841f0a8829f2c0251c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lahmann?= <lahmann@ub.uni-leipzig.de> Date: Thu, 6 Jan 2022 00:45:51 +0100 Subject: [PATCH] refs #20764 [finc] add EZB resolver driver trait in finc for easier mergin and preparation of pull request * make title selector a static variable that can be overwritten * add electronic result type 10 for Unknown Electronic * format rft.date and map rft properties co-authored by: Robert Lange <robert.lange@uni-leipzig.de> --- .../src/finc/Resolver/Driver/EzbTrait.php | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 module/finc/src/finc/Resolver/Driver/EzbTrait.php diff --git a/module/finc/src/finc/Resolver/Driver/EzbTrait.php b/module/finc/src/finc/Resolver/Driver/EzbTrait.php new file mode 100644 index 00000000000..8c74a4a2cec --- /dev/null +++ b/module/finc/src/finc/Resolver/Driver/EzbTrait.php @@ -0,0 +1,254 @@ +<?php +/** + * EZB Link Resolver Driver Trait + * + * EZB is a free service -- the API endpoint is available at + * http://services.dnb.de/fize-service/gvr/full.xml + * + * API documentation is available at + * http://www.zeitschriftendatenbank.de/services/journals-online-print + * + * PHP version 7 + * + * Copyright (C) Markus Fischer, info@flyingfischer.ch + * + * last update: 2011-04-13 + * + * 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_Drivers + * @author Markus Fischer <info@flyingfischer.ch> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki + */ +namespace finc\Resolver\Driver; + +use DOMDocument; +use DOMXpath; + +/** + * EZB Link Resolver Driver + * + * @category VuFind + * @package Resolver_Drivers + * @author Markus Fischer <info@flyingfischer.ch> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki + */ +trait EzbTrait +{ + /** + * As the JOP resolver provides also generic labels 'Article', 'Journal' + * etc. in element AccessLevel this label can be used as title for + * resolver results by setting this variable to 'AccessLevel' + * + * @var string + */ + protected static $xpathTitleSelector = 'Title'; + + /** + * 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', 'Unknown Electronic', $records, $xpath); + + // get results for print, only if available + $this->getPrintResults('2', 'Print available', $records, $xpath); + $this->getPrintResults('3', 'Print partially available', $records, $xpath); + $this->getPrintResults('10', 'Unknown Print', $records, $xpath); + + return $records; + } + + /** + * Downgrade an OpenURL from v1.0 to v0.1 for compatibility with EZB. + * + * @param array $parsed Array of parameters parsed from the OpenURL. + * + * @return string EZB-compatible v0.1 OpenURL + */ + protected function downgradeOpenUrl($parsed) + { + $downgraded = []; + + // prepare content for downgrading + // resolver only accepts date formats YYYY, YYYY-MM, and YYYY-MM-DD + // in case we have a date in another format, drop the date information + if (isset($parsed['rft.date']) + && !preg_match('/^\d{4}(-\d\d(-\d\d)?)?$/', $parsed['rft.date']) + ) { + unset($parsed['rft.date']); + } + + $map = [ + 'rfr_id' => 'sid', + 'rft.date' => 'date', + 'rft.issn' => 'issn', + 'rft.isbn' => 'isbn', // isbn is supported as of 12/2021 + 'rft.volume' => 'volume', + 'rft.issue' => 'issue', + 'rft.spage' => 'spage', + 'rft.pages' => 'pages', + ]; + + // ignore all other parameters + foreach ($parsed as $key => $value) { + // exclude empty parameters + if (isset($value) && $value !== '') { + if (isset($map[$key])) { + $downgraded[] = "{$map[$key]}=$value"; + } elseif (in_array($key, $map)) { + $downgraded[] = "$key=$value"; + } + } + } + if (!empty($downgraded)) { + // we need 'genre' but only the values + // article or journal are allowed... + return "genre=article&" . implode('&', $downgraded); + } + + return implode('&', $parsed); + } + + /** + * 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 . "]" + ); + + /* + * possible state values: + * -1 ISSN nicht eindeutig + * 0 Standort-unabhängig frei zugänglich + * 1 Standort-unabhängig teilweise zugänglich (Unschärfe bedingt durch + * unspezifische Anfrage oder Moving-Wall) + * 2 Lizenziert + * 3 Für gegebene Bibliothek teilweise lizenziert (Unschärfe bedingt durch + * unspezifische Anfrage oder Moving-Wall) + * 4 nicht lizenziert + * 5 Zeitschrift gefunden + * Angaben über Erscheinungsjahr, Datum ... liegen außerhalb des + * hinterlegten bibliothekarischen Zeitraums + * 10 Unbekannt (ISSN unbekannt, Bibliothek unbekannt) + */ + $state_access_mapping = [ + '-1' => 'error', + '0' => 'open', + '1' => 'open', + '2' => 'limited', + '3' => 'limited', + '4' => 'denied', + '5' => 'denied', + '10' => 'unknown' + ]; + + $i = 0; + foreach ($results as $result) { + $record = []; + + // get title from XPath Element defined in $xpathTitleSelector + $titleXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" . + "Result[@state={$state}][" . ($i + 1) . "]/" . + static::$xpathTitleSelector; + $title = $xpath->query($titleXP, $result)->item(0); + if (isset($title)) { + $record['title'] = strip_tags($title->nodeValue); + } + + // get additional coverage information + $additionalXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" . + "Result[@state={$state}][" . ($i + 1) . "]/Additionals/Additional"; + $additionalType = ['nali', 'intervall', 'moving_wall']; + $additionals = []; + foreach ($additionalType as $type) { + $additional = $xpath + ->query($additionalXP . "[@type='" . $type . "']", $result) + ->item(0); + if (isset($additional->nodeValue)) { + $additionals[$type] = strip_tags($additional->nodeValue); + } + } + $record['coverage'] + = !empty($additionals) ? implode("; ", $additionals) : $coverage; + + $record['access'] = $state_access_mapping[$state]; + + // try to find direct access URL + $accessUrlXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" . + "Result[@state={$state}][" . ($i + 1) . "]/AccessURL"; + $accessUrl = $xpath->query($accessUrlXP, $result)->item(0); + + // try to find journal URL as fallback for direct access URL + $journalUrlXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" . + "Result[@state={$state}][" . ($i + 1) . "]/JournalURL"; + $journalUrl = $xpath->query($journalUrlXP, $result)->item(0); + + // return direct access URL if available otherwise journal URL fallback + if (isset($accessUrl->nodeValue)) { + $record['href'] = $accessUrl->nodeValue; + } elseif (isset($journalUrl)) { + $record['href'] = $journalUrl->nodeValue; + } + // 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++; + } + } +} -- GitLab