From 8446089892473efc51fcb2357e1dd7224f17676b 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:49:28 +0100
Subject: [PATCH] refs #20764 [finc] refactoring of EZB resolver driver

* add EZB resolver driver trait in finc for easier mergin and preparation of pull request
* added resolver configuration parameters and comments to local/Resolver.ini
* added new configuration parameters to Resolver.ini for setting ezb/zdb=1
* aligned finc EZB resolver with VuFind EZB resolver
* minor changes in GetResolverLinks->handleRequest to align with VuFind module
* remove ResoverTrait and minify fetchLinks
* inherit title selector for resolver result xpath from vufind

co-authored by: Robert Lange <robert.lange@uni-leipzig.de>
---
 local/config/vufind/Resolver.ini              |  91 +++++++-
 .../src/finc/AjaxHandler/GetResolverLinks.php |  16 +-
 module/finc/src/finc/Resolver/Driver/Ezb.php  | 195 +++++++++---------
 module/finc/src/finc/Resolver/Driver/Redi.php |   3 +-
 .../finc/Resolver/Driver/ResolverTrait.php    |  70 -------
 5 files changed, 195 insertions(+), 180 deletions(-)
 delete mode 100644 module/finc/src/finc/Resolver/Driver/ResolverTrait.php

diff --git a/local/config/vufind/Resolver.ini b/local/config/vufind/Resolver.ini
index 421d09a220e..f9f52132dd8 100644
--- a/local/config/vufind/Resolver.ini
+++ b/local/config/vufind/Resolver.ini
@@ -1,2 +1,91 @@
+;####################################################################
+;##################### DO NOT DELETE THIS HEADER ####################
+;################### Leipzig University Library © 2015 ##############
+;
+; This is the ISIL-instance-specific default INI-file and inherits
+; all the settings from the INI-file defined in [Parent_Config] which
+; points to the default INI-file located in the folder vufind2/local
+;
+
+;[Parent_Config]
+;relative_path = ../../../local/config/vufind/Resolver.ini
+
+; A comma-separated list of config sections from the parent which should be
+; completely overwritten by the equivalent sections in this configuration;
+; any sections not listed here will be merged on a section-by-section basis.
+;override_full_sections = "Ezb"
+
+;
+;       Add instance-specific customization after this header.
+;
+;##################### DO NOT DELETE THIS HEADER ####################
+;####################################################################
+
 [General]
-embed_auto_load = true
\ No newline at end of file
+; configure active resolvers (need to match sections below)
+;active_resolvers = Redi,Ezb
+; VuFind resolver integration settings (apply to all active resolver)
+;window_settings = "toolbar=no,location=no,directories=no,buttons=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=550,height=600"
+;show_in_results = false    ; include in search results
+;show_in_record = false   ; include in core record metadata
+;show_in_holdings = true    ; include in holdings tab of record view
+;embed = true
+embed_auto_load = true
+;replace_other_urls = true
+
+;[Redi]
+;url             = "https://www.redi-bw.de/links/{libraryhandle}"
+;rfr_id          = {library catalog url}
+;resolver        = redi
+
+; If you want to display a graphical link to your link resolver, uncomment the
+; settings below.  graphic should be a URL; graphic_width and graphic_height
+; should be sizes in pixels.
+;graphic = "http://myuniversity.edu/images/findIt.gif"
+;graphic_width = 50
+;graphic_height = 20
+
+; If your link resolver can render an image in response to an OpenURL, you can
+; specify the base URL for image generation here:
+;dynamic_graphic = "http://my-link-resolver/image"
+
+; If dynamic_graphic is set above, the dynamic image can be used instead of the
+; standard text or static-image-based OpenURL link (true), it can be disabled
+; (false), or it can be displayed in addition to the regular link ("both").
+;image_based_linking_mode = both
+
+[Ezb]
+;url             = "https://services.dnb.de/fize-service/gvr/full.xml"
+;rfr_id          = {library catalog url}
+;resolver        = ezb
+
+; If you want to display a graphical link to your link resolver, uncomment the
+; settings below.  graphic should be a URL; graphic_width and graphic_height
+; should be sizes in pixels.
+;graphic = "http://myuniversity.edu/images/findIt.gif"
+;graphic_width = 50
+;graphic_height = 20
+
+; If your link resolver can render an image in response to an OpenURL, you can
+; specify the base URL for image generation here:
+;dynamic_graphic = "http://my-link-resolver/image"
+
+; If dynamic_graphic is set above, the dynamic image can be used instead of the
+; standard text or static-image-based OpenURL link (true), it can be disabled
+; (false), or it can be displayed in addition to the regular link ("both").
+;image_based_linking_mode = both
+
+; EZB resolver driver specific settings
+
+;custom_params[] = "zdbid:getZdbId"
+;custom_params[] = "doi:getDOI"
+;sid             = ""
+;bibid           = ""
+;sigel           = ""
+;isil            = ""
+;bik             = ""
+;validStatesList = "0,1,2,3"
+; Limit resolver results to EZB entries only (default false).
+;ezb_only = true
+; Limit resolver results to ZDB entries only (default false).
+;zdb_only = true
\ No newline at end of file
diff --git a/module/finc/src/finc/AjaxHandler/GetResolverLinks.php b/module/finc/src/finc/AjaxHandler/GetResolverLinks.php
index fc94801bd8f..ab83397f92c 100644
--- a/module/finc/src/finc/AjaxHandler/GetResolverLinks.php
+++ b/module/finc/src/finc/AjaxHandler/GetResolverLinks.php
@@ -173,15 +173,13 @@ class GetResolverLinks extends \VuFind\AjaxHandler\GetResolverLinks
                 ? $resolverObject->getResolverUrl($openUrl) : '';
 
             // Render the links using the view:
-            $html = $this->renderer->render(
-                'ajax/resolverLinks.phtml',
-                compact(
-                    'openUrlBase', 'openUrl', 'print',
-                    'electronic', 'unknown', 'services',
-                    'searchClassId', 'resolver',
-                    'moreOptionsLink'
-                )
-            );
+            $view = [
+                'openUrlBase' => $base, 'openUrl' => $openUrl, 'print' => $print,
+                'electronic' => $electronic, 'services' => $services,
+                'searchClassId' => $searchClassId, 'resolver' => $resolver,
+                'moreOptionsLink' => $moreOptionsLink
+            ];
+            $html = $this->renderer->render('ajax/resolverLinks.phtml', $view);
 
             // output HTML encoded in JSON object
             return $this->formatResponse(compact('html'));
diff --git a/module/finc/src/finc/Resolver/Driver/Ezb.php b/module/finc/src/finc/Resolver/Driver/Ezb.php
index 7af473597cb..f5bd012fa0a 100644
--- a/module/finc/src/finc/Resolver/Driver/Ezb.php
+++ b/module/finc/src/finc/Resolver/Driver/Ezb.php
@@ -6,9 +6,9 @@
  * http://services.dnb.de/fize-service/gvr/full.xml
  *
  * API documentation is available at
- * http://www.zeitschriftendatenbank.de/services/schnittstellen/journals-online-print/
+ * http://www.zeitschriftendatenbank.de/services/journals-online-print
  *
- * PHP version 5
+ * PHP version 7
  *
  * Copyright (C) Markus Fischer, info@flyingfischer.ch
  *
@@ -38,8 +38,8 @@ namespace finc\Resolver\Driver;
 
 use DOMDocument;
 use DOMXpath;
-use \VuFind\I18n\Translator\TranslatorAwareInterface as TranslatorAwareInterface;
-use \VuFind\Resolver\Driver\AbstractBase;
+use VuFind\I18n\Translator\TranslatorAwareInterface as TranslatorAwareInterface;
+use VuFind\Resolver\Driver\Ezb as Base;
 
 /**
  * EZB Link Resolver Driver
@@ -51,10 +51,10 @@ use \VuFind\Resolver\Driver\AbstractBase;
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki
  */
-class Ezb extends AbstractBase implements TranslatorAwareInterface
+class Ezb extends Base implements TranslatorAwareInterface
 {
     use \VuFind\I18n\Translator\TranslatorAwareTrait;
-    use ResolverTrait;
+    use \finc\Resolver\Driver\EzbTrait;
 
     /**
      * Base URL for link resolver
@@ -101,49 +101,6 @@ class Ezb extends AbstractBase implements TranslatorAwareInterface
      */
     public function fetchLinks($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];
-        }
-
-        // 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']);
-        }
-
-        // Downgrade 1.0 to 0.1
-        if ($parsed['ctx_ver'] == 'Z39.88-2004') {
-            $openURL = $this->downgradeOpenUrl($parsed);
-        }
-
-
-        if (isset($this->config->bibid)) {
-            $pid = 'bibid=' . $this->config->bibid;
-        } else {
-            // use IP-based request as fallback
-            $pid = 'client_ip=' . $_SERVER['REMOTE_ADDR'];
-        }
-        $pid .= !isset($parsed['rft.issn']) && isset($parsed['zdbid']) ?
-            '&zdbid=' . $parsed['zdbid'] : '';
-        $pid .= '&ezb=1';
-
-        $openURL .= '&pid=' . urlencode($pid);
-
-        $openURL .= isset($parsed['doi']) ? '&id=doi:' . $parsed['doi'] : '';
-
         $url = $this->getResolverUrl($openURL);
 
         // Make the call to the EZB and load results
@@ -188,6 +145,74 @@ class Ezb extends AbstractBase implements TranslatorAwareInterface
         return $records;
     }
 
+    /**
+     * Get Resolver Url
+     *
+     * Transform the OpenURL as needed to get a working link to the resolver.
+     *
+     * @param string $openURL openURL (url-encoded)
+     *
+     * @return string Link
+     */
+    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);
+        }
+
+        // BOF - finc specific PID-Zone parameter
+
+        // use JOP supported identifier bibid (finc specific as VuFind does not
+        // support resolver specific configuration)
+        if (isset($this->config->bibid)) {
+            $pid = 'bibid=' . $this->config->bibid;
+        } else {
+            // use IP-based request as fallback
+            $pid = 'client_ip=' . $_SERVER['REMOTE_ADDR'];
+        }
+
+        // use finc indexed zdbid if issn is not given
+        $pid .= !isset($parsed['rft.issn']) && isset($parsed['zdbid']) ?
+            '&zdbid=' . $parsed['zdbid'] : '';
+
+        // limit results to ezb only (set to true)
+        if (isset($this->config->ezb_only) && $this->config->ezb_only) {
+            $pid .= 'ezb=1';
+        }
+        // limit results to zdb only (set to true)
+        if (isset($this->config->zdb_only) && $this->config->zdb_only) {
+            $pid .= 'zdb=1';
+        }
+
+        $openURL .= '&pid=' . urlencode($pid);
+
+        // EOF - finc specific PID-Zone parameter
+
+        // as of October 2020 the JOP resolver supports DOIs and we want to use
+        // DOIs for request in finc
+        $openURL .= isset($parsed['doi']) ? '&id=doi:' . $parsed['doi'] : '';
+
+        // Make the call to the EZB and load results
+        $url = $this->baseUrl . '?' . $openURL;
+
+        return $url;
+    }
+
     /**
      * Allows for resolver driver specific enabling/disabling of the more options
      * link which will link directly to the resolver URL. This should return false if
@@ -201,45 +226,6 @@ class Ezb extends AbstractBase implements TranslatorAwareInterface
         // user
         return false;
     }
-
-    /**
-     * 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 = [];
-
-        $map = [
-            'rfr_id' => 'sid',
-            'rft.date' => 'date',
-            'rft.issn' => 'issn',
-            '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)) {
-            return "genre=article&".implode('&', $downgraded);
-        }
-
-        return implode('&',$parsed);
-    }
     
     /**
      * Extract electronic results from the EZB response and inject them into the
@@ -277,8 +263,8 @@ class Ezb extends AbstractBase implements TranslatorAwareInterface
         $state_access_mapping = [
             '-1' => 'error',
             '0'  => 'open',
-            '1'  => 'limited',
-            '2'  => 'open',
+            '1'  => 'open',
+            '2'  => 'limited',
             '3'  => 'limited',
             '4'  => 'denied',
             '5'  => 'denied',
@@ -288,13 +274,16 @@ class Ezb extends AbstractBase implements TranslatorAwareInterface
         $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)."]/Title";
+                "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'];
@@ -311,11 +300,21 @@ class Ezb extends AbstractBase implements TranslatorAwareInterface
 
             $record['access'] = $state_access_mapping[$state];
 
-            $urlXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" .
+            // try to find direct access URL
+            $accessUrlXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" .
                 "Result[@state={$state}][".($i+1)."]/AccessURL";
-            $url = $xpath->query($urlXP, $result)->item(0);
-            if (isset($url->nodeValue)) {
-                $record['href'] = $url->nodeValue;
+            $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 as fallback
+            if (isset($accessUrl->nodeValue)) {
+                $record['href'] = $accessUrl->nodeValue;
+            } elseif (isset($journalUrl)) {
+                $record['href'] = $journalUrl->nodeValue;
             }
 
             $readmeXP = "/OpenURLResponseXML/Full/ElectronicData/ResultList/" .
@@ -379,19 +378,19 @@ class Ezb extends AbstractBase implements TranslatorAwareInterface
             $record['title'] = $coverage;
 
             $resultXP = "/OpenURLResponseXML/Full/PrintData/ResultList/" .
-                "Result[@state={$state}][".($i+1)."]";
+                "Result[@state={$state}][" . ($i + 1) . "]";
             $resultElements = [
                 'Title', 'Location', 'Signature', 'Period', 'Holding_comment'
             ];
             $elements = [];
             foreach ($resultElements as $element) {
-                $elem = $xpath->query($resultXP . "/".$element, $result)->item(0);
+                $elem = $xpath->query($resultXP . "/" . $element, $result)->item(0);
                 if (isset($elem->nodeValue)) {
                     $elements[$element] = strip_tags($elem->nodeValue);
                 }
             }
             $record['coverage']
-                = !empty($elements) ? implode(";", $elements) : $coverage;
+                = !empty($elements) ? implode("; ", $elements) : $coverage;
 
             $record['access'] = $state_access_mapping[$state];
 
diff --git a/module/finc/src/finc/Resolver/Driver/Redi.php b/module/finc/src/finc/Resolver/Driver/Redi.php
index 6bd0a2d30d8..c95c4a029c8 100644
--- a/module/finc/src/finc/Resolver/Driver/Redi.php
+++ b/module/finc/src/finc/Resolver/Driver/Redi.php
@@ -15,7 +15,7 @@
  */
 namespace finc\Resolver\Driver;
 
-use \VuFind\Resolver\Driver\Redi as RediBase;
+use VuFind\Resolver\Driver\Redi as RediBase;
 
 /**
  * Redi Link Resolver Driver
@@ -29,7 +29,6 @@ use \VuFind\Resolver\Driver\Redi as RediBase;
  */
 class Redi extends RediBase
 {
-    use ResolverTrait;
 
     /**
      * Constructor
diff --git a/module/finc/src/finc/Resolver/Driver/ResolverTrait.php b/module/finc/src/finc/Resolver/Driver/ResolverTrait.php
deleted file mode 100644
index 29ad59d61ef..00000000000
--- a/module/finc/src/finc/Resolver/Driver/ResolverTrait.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-/**
- * Finc Resolver Trait
- *
- * PHP version 5
- *
- * Copyright (C) Leipzig University Library 2015
- *
- * 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.
- *
- * @category VuFind
- * @package  Resolver_Drivers
- * @author   Robert Lange <lange@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 \Zend\Config\Exception\InvalidArgumentException;
-
-/**
- * Finc Link Resolver Driver
- *
- * @category VuFind
- * @package  Resolver_Drivers
- * @author   Robert Lange <lange@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 ResolverTrait
-{
-    /**
-     * Get Resolver Url
-     *
-     * Transform the OpenURL as needed to get a working link to the resolver.
-     *
-     * @param string $openURL openURL (url-encoded)
-     *
-     * @return string Returns resolver specific url
-     */
-    public function getResolverUrl($openURL)
-    {
-        if (empty($this->baseUrl)) {
-            throw new InvalidArgumentException(
-                'Base url for link resolver is not correctly configured.'
-            );
-        }
-
-        if (empty($openURL)) {
-            return $this->baseUrl;
-        }
-
-        if ($openURL[0] === '?' || $openURL[0] === '&') {
-            $openURL = substr($openURL, 1, strlen($openURL)-1);
-        }
-
-        if (strpos($this->baseUrl, '?') === false) {
-            return $this->baseUrl . '?' . $openURL;
-        } else {
-            return $this->baseUrl . '&' . $openURL;
-        }
-    }
-}
-- 
GitLab