Skip to content
Snippets Groups Projects
Ezb.php 12 KiB
Newer Older
<?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;
    /**
     * 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';
            }
            $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++;
        }
    }