diff --git a/local/alpha/languages/de.ini b/local/alpha/languages/de-finc.ini similarity index 93% rename from local/alpha/languages/de.ini rename to local/alpha/languages/de-finc.ini index 6d0ad684ee9fa41d9b6d600d022dc0cdcf605397..21b1660c482df07e52c0b9d3249474302f2ed812 100644 --- a/local/alpha/languages/de.ini +++ b/local/alpha/languages/de-finc.ini @@ -8,7 +8,7 @@ ; vufind2/local/languages ; -@parent_ini = "../../languages/de.ini" +@parent_ini = "../../languages/de-finc.ini" ; ; Add ALPHA-specific customization after this header. diff --git a/local/config/vufind/DAIA.ini b/local/config/vufind/DAIA.ini index 7ca1a3eedd26266590d6b8f00e06130a203a953f..aad984c296a382b231ad5b5b73fcc300acc9bd44 100644 --- a/local/config/vufind/DAIA.ini +++ b/local/config/vufind/DAIA.ini @@ -1,4 +1,38 @@ -[DAIA] +; DAIA driver expects a DAIA Query API as specified in: +; http://gbv.github.io/daiaspec/daia.html#query-api +; +; The settings in the [DAIA] section will be used for all DAIA requests. +; The name of this section got refactored with VuFind 2.4, although the old +; configuration using the [Global] section still works, it should be replaced +; with the new [DAIA] section. +; Note: Settings for daiaResponseFormat and daiaIdPrefix are not supported if +; a pre VuFind 2.4 configuration with the [Global] section is used. +; i.e.: +; [Global] +; baseUrl = [your DAIA server base url] +; daiaIdPrefix = [this setting will have no effect] +; daiaResponseFormat = [this setting will have no effect] +; +; A default DAIA call looks like this: +; https://daia.myuniversity.edu/?id=ppn:12345678&format=json +; +; This default DAIA call would be configured as: +; [DAIA] +; baseUrl = https://daia.myuniversity.edu +; daiaidprefix = ppn: +; daiaResponseFormat = json +; + +; The prefix prepended to the VuFind record Id resulting in the document URI +; used for the DAIA request (default = ppn:) (the prefix usually defines the +; field which the DAIA server uses for the loookup - e.g. ppn: or isbn:). +;daiaIdPrefix = "ppn:" + +; Set the requested DAIA response format: xml (default), json +;daiaResponseFormat = xml + +[Global] +; The base URL for the DAIA webservice. ; refer to http://data.ub.uni-leipzig.de/ldinfo for setting up specific DAIA-urls baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ diff --git a/local/config/vufind/FincDAIA.ini b/local/config/vufind/FincDAIA.ini index 2505ef8147a06dd44f65246c63d95904fe63cc05..3ecfc707be66a67f0770fb0f6e6c0595360f7a89 100644 --- a/local/config/vufind/FincDAIA.ini +++ b/local/config/vufind/FincDAIA.ini @@ -1,8 +1,8 @@ -[DAIA] +;[DAIA] ; this config configures the extended DAIA-Driver FincDAIA ; refer to ticket #4499 for further info on configuring it -baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ -ilsIdentifier = "default" +;baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ +;ilsIdentifier = "default" ;ISIL = "ISIL" ; config-examples: @@ -17,4 +17,9 @@ ilsIdentifier = "default" ; ilsIdentifier = "record_id" ; default -; ilsIdentifier = "default" \ No newline at end of file +; ilsIdentifier = "default" + +[Global] +; The base URL for the DAIA webservice. +; refer to http://data.ub.uni-leipzig.de/ldinfo for setting up specific DAIA-urls +baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ diff --git a/local/dev/languages/de.ini b/local/dev/languages/de-finc.ini similarity index 93% rename from local/dev/languages/de.ini rename to local/dev/languages/de-finc.ini index 82610eb5926df679c2765b2e3d347e3c782d3101..1005ae7daf017f7b341a5e1c9c1e5405da2eb6e0 100644 --- a/local/dev/languages/de.ini +++ b/local/dev/languages/de-finc.ini @@ -8,7 +8,7 @@ ; vufind2/local/languages ; -@parent_ini = "../../languages/de.ini" +@parent_ini = "../../languages/de-finc.ini" ; ; Add DEV-specific customization after this header. diff --git a/local/languages/de.ini b/local/languages/de-finc.ini similarity index 99% rename from local/languages/de.ini rename to local/languages/de-finc.ini index a03b05e9b0de1de75f0583866e0a5e44bd4b55d2..21c761cb6827fa27d94735054a16e4e97ac772d2 100644 --- a/local/languages/de.ini +++ b/local/languages/de-finc.ini @@ -1,3 +1,4 @@ +@parent_ini = "../../../languages/de.ini" ; Formate entsprechend der format_map_de15.properties Book = Buch eBook = E-Book diff --git a/module/finc/src/finc/ILS/Driver/DAIA.php b/module/finc/src/finc/ILS/Driver/DAIA.php index 37b2f24d4e8890aa41f1131c6acb16db202696eb..99894919879c70e1ea4ba662714dbd5e978d4ff4 100644 --- a/module/finc/src/finc/ILS/Driver/DAIA.php +++ b/module/finc/src/finc/ILS/Driver/DAIA.php @@ -3,10 +3,11 @@ * ILS Driver for VuFind to query availability information via DAIA. * * Based on the proof-of-concept-driver by Till Kinstler, GBV. + * Relaunch of the daia driver developed by Oliver Goldschmidt. * * PHP version 5 * - * Copyright (C) Oliver Goldschmidt 2010. + * Copyright (C) Jochen Lienhard 2014. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -23,65 +24,72 @@ * * @category VuFind2 * @package ILS_Drivers + * @author Jochen Lienhard <lienhard@ub.uni-freiburg.de> * @author Oliver Goldschmidt <o.goldschmidt@tu-harburg.de> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki */ namespace finc\ILS\Driver; -use DOMDocument, VuFind\Exception\ILS as ILSException, Zend\Log\LoggerInterface; +use DOMDocument, VuFind\Exception\ILS as ILSException, + VuFindHttp\HttpServiceAwareInterface as HttpServiceAwareInterface, + Zend\Log\LoggerAwareInterface as LoggerAwareInterface, + Zend\Log\LoggerInterface as LoggerInterface; /** * ILS Driver for VuFind to query availability information via DAIA. * - * Based on the proof-of-concept-driver by Till Kinstler, GBV. - * * @category VuFind2 * @package ILS_Drivers + * @author Jochen Lienhard <lienhard@ub.uni-freiburg.de> * @author Oliver Goldschmidt <o.goldschmidt@tu-harburg.de> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki */ -class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAwareInterface +class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareInterface, LoggerAwareInterface { /** - * Base URL + * Base URL for DAIA Service * * @var string */ - protected $baseURL; + protected $baseUrl; /** - * Logger (or false for none) + * DAIA query identifier prefix * - * @var LoggerInterface|bool + * @var string */ - protected $logger = false; + protected $daiaIdPrefix; /** - * Set the logger + * DAIA response format * - * @param LoggerInterface $logger Logger to use. + * @var string + */ + protected $daiaResponseFormat; + + /** + * DAIA legacySupport flag * - * @return void + * @var boolean */ - public function setLogger(LoggerInterface $logger) - { - $this->logger = $logger; - } + protected $legacySupport = false; /** - * Log a debug message. + * Logger (or false for none) * - * @param string $msg Message to log. + * @var LoggerInterface|bool + */ + protected $logger = false; + + /** + * HTTP service * - * @return void + * @var \VuFindHttp\HttpServiceInterface */ - protected function debug($msg) - { - if ($this->logger) { - $this->logger->debug(get_class($this) . ": $msg"); - } - } + protected $httpService = null; /** * Initialize the driver. @@ -94,11 +102,45 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw */ public function init() { - if (!isset($this->config['DAIA']['baseUrl'])) { + // DAIA.ini sections changed, therefore move old [Global] section to + // new [DAIA] section as fallback + if (isset($this->config['Global']) && !isset($this->config['DAIA'])) { + $this->config['DAIA'] = $this->config['Global']; + $this->legacySupport = true; + } + + if (isset($this->config['DAIA']['baseUrl'])) { + $this->baseUrl = $this->config['DAIA']['baseUrl']; + } else { throw new ILSException('DAIA/baseUrl configuration needs to be set.'); } + if (isset($this->config['DAIA']['daiaResponseFormat'])) { + $this->daiaResponseFormat = strtolower( + $this->config['DAIA']['daiaResponseFormat'] + ); + } else { + $this->debug("No daiaResponseFormat setting found, using default: xml"); + $this->daiaResponseFormat = "xml"; + } + if (isset($this->config['DAIA']['daiaIdPrefix'])) { + $this->daiaIdPrefix = $this->config['DAIA']['daiaIdPrefix']; + } else { + $this->debug("No daiaIdPrefix setting found, using default: ppn:"); + $this->daiaIdPrefix = "ppn:"; + } + } - $this->baseURL = $this->config['DAIA']['baseUrl']; + /** + * Public Function which retrieves renew, hold and cancel settings from the + * driver ini file. + * + * @param string $function The name of the feature to be checked + * + * @return array An array with key-value pairs. + */ + public function getConfig($function) + { + return isset($this->config[$function]) ? $this->config[$function] : false; } /** @@ -112,6 +154,7 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw * @param array $details Item details from getHoldings return array * * @return string URL to ILS's OPAC's place hold screen. + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getHoldLink($id, $details) @@ -133,8 +176,13 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw */ public function getStatus($id) { - $holding = $this->daiaToHolding($id); - return $holding; + if ($this->daiaResponseFormat == 'xml') { + return $this->getXMLStatus($id); + } elseif ($this->daiaResponseFormat == 'json') { + return $this->getJSONStatus($id); + } else { + throw new ILSException('No matching format found for status retrieval.'); + } } /** @@ -150,24 +198,21 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw */ public function getStatuses($ids) { - $items = array(); - foreach ($ids as $id) { - $items[] = $this->getShortStatus($id); + $items = []; + + if ($this->daiaResponseFormat == 'xml') { + foreach ($ids as $id) { + $items[] = $this->getXMLShortStatus($id); + } + } elseif ($this->daiaResponseFormat == 'json') { + foreach ($ids as $id) { + $items[] = $this->getJSONStatus($id); + } + } else { + throw new ILSException('No matching format found for status retrieval.'); } - return $items; - } - /** - * Public Function which retrieves renew, hold and cancel settings from the - * driver ini file. - * - * @param string $function The name of the feature to be checked - * - * @return array An array with key-value pairs. - */ - public function getConfig($function) - { - return isset($this->config[$function]) ? $this->config[$function] : false; + return $items; } /** @@ -203,25 +248,206 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw */ public function getPurchaseHistory($id) { - return array(); + return []; } /** - * Query a DAIA server and return the result as DOMDocument object. - * The returned object is an XML document containing - * content as described in the DAIA format specification. + * Set the HTTP service to be used for HTTP requests. * - * @param string $id Document to look up. + * @param HttpServiceInterface $service HTTP service * - * @return DOMDocument Object representation of an XML document containing - * content as described in the DAIA format specification. + * @return void */ - protected function queryDAIA($id) + public function setHttpService(\VuFindHttp\HttpServiceInterface $service) { - $daia = new DOMDocument(); - $daia->load($this->baseURL . $id); + $this->httpService = $service; + } + + /** + * Perform an HTTP request. + * + * @param string $id id for query in daia + * + * @return xml or json object + * @throws ILSException + */ + protected function doHTTPRequest($id) + { + $contentTypes = [ + "xml" => "application/xml", + "json" => "application/json", + ]; + + $http_headers = [ + "Content-type: " . $contentTypes[$this->daiaResponseFormat], + "Accept: " . $contentTypes[$this->daiaResponseFormat] + ]; + + $params = [ + "id" => $this->daiaIdPrefix . $id, + "format" => $this->daiaResponseFormat, + ]; + + try { + if ($this->legacySupport) { + // HttpRequest for DAIA legacy support as all + // the parameters are contained in the baseUrl + $result = $this->httpService->get( + $this->baseUrl . $id, + [], null, $http_headers + ); + } else { + $result = $this->httpService->get( + $this->baseUrl, + $params, null, $http_headers + ); + } + } catch (\Exception $e) { + throw new ILSException($e->getMessage()); + } + + if (!$result->isSuccess()) { + // throw ILSException disabled as this will be shown in VuFind-Frontend + //throw new ILSException('HTTP error ' . $result->getStatusCode() . + // ' retrieving status for record: ' . $id); + // write to Debug instead + $this->debug( + 'HTTP status ' . $result->getStatusCode() . + ' received, retrieving availability information for record: ' . $id + ); + + // return false as DAIA request failed + return false; + } + return ($result->getBody()); + + } + + /** + * Get Status of JSON Result + * + * This method gets a json result from the DAIA server and + * analyses it. Than a vufind result is build. + * + * @param string $id The id of the bib record + * + * @return array() of items + */ + protected function getJSONStatus($id) + { + // get daia json request for id and decode it + $daia = json_decode($this->doHTTPRequest($id), true); + $result = []; + if (array_key_exists("message", $daia)) { + // analyse the message for the error handling and debugging + } + if (array_key_exists("instituion", $daia)) { + // information about the institution that grants or + // knows about services and their availability + // this fields could be analyzed: href, content, id + } + if (array_key_exists("document", $daia)) { + // analyse the items + $dummy_item = ["id" => "0815", + "availability" => true, + "status" => "Available", + "location" => "physical location no HTML", + "reserve" => "N", + "callnumber" => "007", + "number" => "1", + "item_id" => "0815", + "barcode" => "1"]; + // each document may contain: id, href, message, item + foreach ($daia["document"] as $document) { + $doc_id = null; + $doc_href = null; + $doc_message = null; + if (array_key_exists("id", $document)) { + $doc_id = $document["id"]; + } + if (array_key_exists("href", $document)) { + // url of the document + $doc_href = $document["href"]; + } + if (array_key_exists("message", $document)) { + // array of messages with language code and content + $doc_message = $document["message"]; + } + // if one or more items exist, iterate and build result-item + if (array_key_exists("item", $document)) { + $number = 0; + foreach ($document["item"] as $item) { + $result_item = []; + $result_item["id"] = $id; + $result_item["item_id"] = $id; + $number++; // count items + $result_item["number"] = $number; + // set default value for barcode + $result_item["barcode"] = "1"; + // set default value for reserve + $result_item["reserve"] = "N"; + // get callnumber + if (isset($item["label"])) { + $result_item["callnumber"] = $item["label"]; + } else { + $result_item["callnumber"] = "Unknown"; + } + // get location + if (isset($item["storage"])) { + $result_item["location"] = $item["storage"]["content"]; + } else { + $result_item["location"] = "Unknown"; + } + // status and availability will be calculated in own function + $result_item = $this->calculateStatus($item)+$result_item; + // add result_item to the result array + $result[] = $result_item; + } // end iteration on item + } + } // end iteration on document + // $result[]=$dummy_item; + } + return $result; + } - return $daia; + /** + * Calaculate Status and Availability of an item + * + * If availability is false the string of status will be shown in vufind + * + * @param string $item json DAIA item + * + * @return array("status"=>"only for VIPs" ... ) + */ + protected function calculateStatus($item) + { + $availability = false; + $status = null; + $duedate = null; + if (array_key_exists("available", $item)) { + // check if item is loanable or presentation + foreach ($item["available"] as $available) { + if ($available["service"] == "loan") { + $availability = true; + } + if ($available["service"] == "presentation") { + $availability = true; + } + } + } + if (array_key_exists("unavailable", $item)) { + foreach ($item["unavailable"] as $unavailable) { + if ($unavailable["service"] == "loan") { + if (isset($unavailable["expected"])) { + $duedate = $unavailable["expected"]; + } + $status = "dummy text"; + } + } + } + return (["status" => $status, + "availability" => $availability, + "duedate" => $duedate]); } /** @@ -231,36 +457,48 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw * * @return array */ - protected function daiaToHolding($id) + protected function getXMLStatus($id) { - $daia = $this->queryDAIA($id); + $daia = new DOMDocument(); + $response = $this->doHTTPRequest($id); + if ($response) { + $daia->loadXML($response); + } // get Availability information from DAIA $documentlist = $daia->getElementsByTagName('document'); - $status = array(); + + // handle empty DAIA response + if ($documentlist->length == 0 + && $daia->getElementsByTagName("message") != null + ) { + // analyse the message for the error handling and debugging + } + + $status = []; for ($b = 0; $documentlist->item($b) !== null; $b++) { $itemlist = $documentlist->item($b)->getElementsByTagName('item'); - $ilslink=''; - if ($documentlist->item($b)->attributes->getNamedItem('href')!==null) { + $ilslink = ''; + if ($documentlist->item($b)->attributes->getNamedItem('href') !== null) { $ilslink = $documentlist->item($b)->attributes ->getNamedItem('href')->nodeValue; } - $emptyResult = array( - 'callnumber' => '-', - 'availability' => '0', - 'number' => 1, - 'reserve' => 'No', - 'duedate' => '', - 'queue' => '', - 'delay' => '', - 'barcode' => 'No samples', - 'status' => '', - 'id' => $id, - 'location' => '', - 'ilslink' => $ilslink, - 'label' => 'No samples' - ); + $emptyResult = [ + 'callnumber' => '-', + 'availability' => '0', + 'number' => 1, + 'reserve' => 'No', + 'duedate' => '', + 'queue' => '', + 'delay' => '', + 'barcode' => 'No samples', + 'status' => '', + 'id' => $id, + 'location' => '', + 'ilslink' => $ilslink, + 'label' => 'No samples' + ]; for ($c = 0; $itemlist->item($c) !== null; $c++) { - $result = array( + $result = [ 'callnumber' => '', 'availability' => '0', 'number' => ($c+1), @@ -277,8 +515,8 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw 'location.id' => '', 'location.href' => '', 'label' => '', - 'notes' => array() - ); + 'notes' => [], + ]; if ($itemlist->item($c)->attributes->getNamedItem('id') !== null) { $result['item_id'] = $itemlist->item($c)->attributes ->getNamedItem('id')->nodeValue; @@ -306,8 +544,13 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw $result['location'] = $storageElements->item(0)->nodeValue; //$result['location.id'] = $storageElements->item(0) // ->attributes->getNamedItem('id')->nodeValue; - $result['location.href'] = $storageElements->item(0) - ->attributes->getNamedItem('href')->nodeValue; + $href = $storageElements->item(0)->attributes + ->getNamedItem('href'); + if ($href !== null) { + //href attribute is recommended but not mandatory + $result['location.href'] = $storageElements->item(0) + ->attributes->getNamedItem('href')->nodeValue; + } //$result['barcode'] = $result['location.id']; } } @@ -377,7 +620,8 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw $result['loan.availability'] = '0'; $result['loan_availability'] = '0'; if ($expectedNode !== null) { - $result['loan.duedate'] = $expectedNode->nodeValue; + $result['loan.duedate'] + = $expectedNode->nodeValue; } if ($queueNode !== null) { $result['loan.queue'] = $queueNode->nodeValue; @@ -390,7 +634,8 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw = $expectedNode->nodeValue; } if ($queueNode !== null) { - $result['interloan.queue'] = $queueNode->nodeValue; + $result['interloan.queue'] + = $queueNode->nodeValue; } $result['availability'] = '0'; } elseif ($service === 'openaccess') { @@ -400,7 +645,8 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw = $expectedNode->nodeValue; } if ($queueNode !== null) { - $result['openaccess.queue'] = $queueNode->nodeValue; + $result['openaccess.queue'] + = $queueNode->nodeValue; } $result['availability'] = '0'; } @@ -443,13 +689,15 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw } elseif ($service === 'interloan') { $result['interloan.availability'] = '1'; if ($delayNode !== null) { - $result['interloan.delay'] = $delayNode->nodeValue; + $result['interloan.delay'] + = $delayNode->nodeValue; } $result['availability'] = '1'; } elseif ($service === 'openaccess') { $result['openaccess.availability'] = '1'; if ($delayNode !== null) { - $result['openaccess.delay'] = $delayNode->nodeValue; + $result['openaccess.delay'] + = $delayNode->nodeValue; } $result['availability'] = '1'; } @@ -497,23 +745,27 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw * id, availability (boolean), status, location, reserve, callnumber, duedate, * number */ - public function getShortStatus($id) + public function getXMLShortStatus($id) { - $daia = $this->queryDAIA($id); + $daia = new DOMDocument(); + $response = $this->doHTTPRequest($id); + if ($response) { + $daia->loadXML($response); + } // get Availability information from DAIA $itemlist = $daia->getElementsByTagName('item'); $label = "Unknown"; $storage = "Unknown"; $presenceOnly = '1'; - $holding = array(); + $holding = []; for ($c = 0; $itemlist->item($c) !== null; $c++) { $earliest_href = ''; $storageElements = $itemlist->item($c)->getElementsByTagName('storage'); - if ($storageElements->item(0)->nodeValue) { + if ($storageElements->item(0) && $storageElements->item(0)->nodeValue) { if ($storageElements->item(0)->nodeValue === 'Internet') { $href = $storageElements->item(0)->attributes ->getNamedItem('href')->nodeValue; - $storage = '<a href="'.$href.'">'.$href.'</a>'; + $storage = '<a href="' . $href . '">' . $href . '</a>'; } else { $storage = $storageElements->item(0)->nodeValue; } @@ -544,14 +796,14 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw $unavailableElements = $itemlist->item($c) ->getElementsByTagName('unavailable'); if ($unavailableElements->item(0) !== null) { - $earliest = array(); - $queue = array(); - $hrefs = array(); + $earliest = []; + $queue = []; + $hrefs = []; for ($n = 0; $unavailableElements->item($n) !== null; $n++) { $unavailHref = $unavailableElements->item($n)->attributes ->getNamedItem('href'); if ($unavailHref !== null) { - $hrefs['item'.$n] = $unavailHref->nodeValue; + $hrefs['item' . $n] = $unavailHref->nodeValue; } $expectedNode = $unavailableElements->item($n)->attributes ->getNamedItem('expected'); @@ -566,14 +818,14 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw // 'expected' => $expectedNode->nodeValue, // 'recall' => $unavailHref->nodeValue); //array_push($earliest, $expectedNode->nodeValue); - $earliest['item'.$n] = $expectedNode->nodeValue; + $earliest['item' . $n] = $expectedNode->nodeValue; } else { array_push($earliest, "0"); } $queueNode = $unavailableElements->item($n)->attributes ->getNamedItem('queue'); if ($queueNode !== null) { - $queue['item'.$n] = $queueNode->nodeValue; + $queue['item' . $n] = $queueNode->nodeValue; } else { array_push($queue, "0"); } @@ -585,8 +837,10 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw foreach ($earliest as $earliest_key => $earliest_value) { if ($earliest_counter === 0) { $earliest_duedate = $earliest_value; - $earliest_href = $hrefs[$earliest_key]; - $earliest_queue = $queue[$earliest_key]; + $earliest_href = isset($hrefs[$earliest_key]) + ? $hrefs[$earliest_key] : ''; + $earliest_queue = isset($queue[$earliest_key]) + ? $queue[$earliest_key] : ''; } $earliest_counter = 1; } @@ -602,28 +856,56 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements \Zend\Log\LoggerAw $status = 'missing'; } } - if (!$status) { + if (!isset($status)) { $status = 'Unavailable'; } $availability = 0; } $reserve = 'N'; - if ($earliest_queue > 0) { + if (isset($earliest_queue) && $earliest_queue > 0) { $reserve = 'Y'; } - $holding[] = array('availability' => $availability, - 'id' => $id, - 'status' => "$status", - 'location' => "$storage", - 'reserve' => $reserve, - 'queue' => $earliest_queue, - 'callnumber' => "$label", - 'duedate' => $earliest_duedate, - 'leanable' => $leanable, - 'recallhref' => $earliest_href, - 'number' => ($c+1), - 'presenceOnly' => $presenceOnly); + $holding[] = [ + 'availability' => $availability, + 'id' => $id, + 'status' => isset($status) ? "$status" : '', + 'location' => isset($storage) ? "$storage" : '', + 'reserve' => isset($reserve) ? $reserve : '', + 'queue' => isset($earliest_queue) ? $earliest_queue : '', + 'callnumber' => isset($label) ? "$label" : '', + 'duedate' => isset($earliest_duedate) ? $earliest_duedate : '', + 'leanable' => isset($leanable) ? $leanable : '', + 'recallhref' => isset($earliest_href) ? $earliest_href : '', + 'number' => ($c+1), + 'presenceOnly' => isset($presenceOnly) ? $presenceOnly : '', + ]; } return $holding; } -} \ No newline at end of file + + /** + * Set the logger + * + * @param LoggerInterface $logger Logger to use. + * + * @return void + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Log a debug message. + * + * @param string $msg Message to log. + * + * @return void + */ + protected function debug($msg) + { + if ($this->logger) { + $this->logger->debug(get_class($this) . ": $msg"); + } + } +} diff --git a/module/finc/src/finc/ILS/Driver/FincDAIA.php b/module/finc/src/finc/ILS/Driver/FincDAIA.php index 57c71714ea7b862cb3f1ecaff4d048c26015cbac..52e322738281c2f59402cc5693d69bafb11d2c9a 100644 --- a/module/finc/src/finc/ILS/Driver/FincDAIA.php +++ b/module/finc/src/finc/ILS/Driver/FincDAIA.php @@ -41,7 +41,7 @@ use DOMDocument, VuFind\Exception\ILS as ILSException; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki */ -class FincDAIA extends DAIA implements \Zend\Log\LoggerAwareInterface +class FincDAIA extends PAIA implements \Zend\Log\LoggerAwareInterface { /** * Identifier used for interaction with ILS @@ -85,107 +85,74 @@ class FincDAIA extends DAIA implements \Zend\Log\LoggerAwareInterface */ public function init() { - if (!isset($this->config['DAIA']['baseUrl'])) { - throw new ILSException('DAIA/baseUrl configuration needs to be set.'); - } - - $this->baseURL = $this->config['DAIA']['baseUrl']; + parent::init(); + + // due to section naming changes in DAIA.ini + // switch legacySupport + if ($this->legacySupport) { + // set the ILS-specific recordId for interaction with ILS + // get the ILS-specific identifier + if (!isset($this->config['Global']['ilsIdentifier'])) { + $this->debug("No ILS-specific identifier configured, setting ilsIdentifier=default."); + $this->ilsIdentifier = "default"; + } else { + $this->ilsIdentifier = $this->config['Global']['ilsIdentifier']; + } - // set the ILS-specific recordId for interaction with ILS - // get the ILS-specific identifier - if (!isset($this->config['DAIA']['ilsIdentifier'])) { - $this->debug("No ILS-specific identifier configured, setting ilsIdentifier=default."); - $this->ilsIdentifier = "default"; + // get ISIL from config if ILS-specific recordId is barcode for interaction with ILS + // get the ILS-specific identifier + if (!isset($this->config['Global']['ISIL'])) { + $this->debug("No ISIL for ILS-driver configured."); + $this->isil = ''; + } else { + $this->isil = $this->config['Global']['ISIL']; + } } else { - $this->ilsIdentifier = $this->config['DAIA']['ilsIdentifier']; - } + // set the ILS-specific recordId for interaction with ILS + // get the ILS-specific identifier + if (!isset($this->config['DAIA']['ilsIdentifier'])) { + $this->debug("No ILS-specific identifier configured, setting ilsIdentifier=default."); + $this->ilsIdentifier = "default"; + } else { + $this->ilsIdentifier = $this->config['DAIA']['ilsIdentifier']; + } - // get ISIL from config if ILS-specific recordId is barcode for interaction with ILS - // get the ILS-specific identifier - if (!isset($this->config['DAIA']['ISIL'])) { - $this->debug("No ISIL for ILS-driver configured."); - $this->isil = ''; - } else { - $this->isil = $this->config['DAIA']['ISIL']; + // get ISIL from config if ILS-specific recordId is barcode for interaction with ILS + // get the ILS-specific identifier + if (!isset($this->config['DAIA']['ISIL'])) { + $this->debug("No ISIL for ILS-driver configured."); + $this->isil = ''; + } else { + $this->isil = $this->config['DAIA']['ISIL']; + } } } /** - * Get the Record-Object from the RecordDriver. - * - * @param string $id ID of record to retrieve - * - * @return \VuFind\RecordDriver\AbstractBase - */ - public function getRecord($id) - { - return $this->recordLoader->load($id); - } - - /** - * Get Status - * - * This is responsible for retrieving the status information of a certain - * record. - * - * @param string $id The record id to retrieve the holdings for - * - * @throws ILSException - * @return mixed On success, an associative array with the following keys: - * id, availability (boolean), status, location, reserve, callnumber. - */ - public function getStatus($id) - { - $holding = $this->daiaToHolding($this->getILSRecordId($id)); - return $holding; - } - - /** - * Get Statuses - * - * This is responsible for retrieving the status information for a - * collection of records. + * Wrapper implementation of @queryDAIAXML($id) to perform the query + * with the ILS-specific identifier. * - * @param array $ids The array of record ids to retrieve the status for + * @param string $id Document to look up. * - * @throws ILSException - * @return array An array of getStatus() return values on success. + * @return DOMDocument Object representation of an XML document containing + * content as described in the DAIA format specification. */ - public function getStatuses($ids) + protected function doHTTPRequest($id) { - $items = array(); - foreach ($ids as $id) { - $items[] = $this->getShortStatus($this->getILSRecordId($id)); - } - return $items; + return parent::doHTTPRequest($this->getILSRecordId($id)); } /** - * Query a DAIA server and return the result as DOMDocument object. - * The returned object is an XML document containing - * content as described in the DAIA format specification. + * Get the Record-Object from the RecordDriver. * - * @param string $id Document to look up. + * @param string $id ID of record to retrieve * - * @return DOMDocument Object representation of an XML document containing - * content as described in the DAIA format specification. + * @return \VuFind\RecordDriver\AbstractBase */ - protected function queryDAIA($id) + protected function getRecord($id) { - $opts = array( - 'http' => array( - 'ignore_errors' => 'true', - ) - ); - - $context = stream_context_create($opts); - libxml_set_streams_context($context); - - $daia = new DOMDocument(); - $daia->load($this->baseURL . $id); - - return $daia; + return $this->recordLoader->load($id); } /** @@ -198,307 +165,23 @@ class FincDAIA extends DAIA implements \Zend\Log\LoggerAwareInterface protected function getILSRecordId($id) { //get the ILS-specific recordId - if ($this->ilsIdentifier == "default") { - return $id; - } else { + if ($this->ilsIdentifier != "default") { $ilsRecordId = $this->getRecord($id)->getILSIdentifier($this->ilsIdentifier); - if ($ilsRecordId == '') - { + if ($ilsRecordId == '') { return $id; } else { if (is_array($ilsRecordId)) { // use ISIL for identifying the correct ILS-identifier if array is returned foreach ($ilsRecordId as $recordId) { - if (preg_match($recordId, "/^(".$this->isil.").*$/")) { + if (preg_match("/^(\(".$this->isil."\)).*$/", $recordId)) { return substr($recordId, strpos($recordId, "(".$this->isil.")")+strlen("(".$this->isil.")")); } } } - return $ilsRecordId; } - - // DAIA Request with PPN from MarcRecord - //$daia = $this->queryDAIA($this->getSolrRecord($id)->getFincPPN()->getData()); - - // DAIA Request with PPN from Solr - //$daia = $this->queryDAIA($this->getSolrRecord($id)->getFincPPNSolr()); - - // DAIA Request with barcode - //$daia = $this->queryDAIA($this->getSolrRecord($id)->getFincBarcode()); - } - } - - /** - * Flatten a DAIA response to an array of holding information. - * - * @param string $id Document to look up. - * - * @return array - */ - protected function daiaToHolding($id) - { - $daia = $this->queryDAIA($id); - // get Availability information from DAIA - $documentlist = $daia->getElementsByTagName('document'); - - // handle empty DAIA response - if ($documentlist->length == 0 && - $daia->getElementsByTagName("message")->item(0)->attributes->getNamedItem("errno")->nodeValue == "404") { - $this->debug("Error: " . $daia->getElementsByTagName("message")->item(0)->attributes->getNamedItem("errno")->nodeValue - . " reported for DAIA request"); - } - - $status = array(); - for ($b = 0; $documentlist->item($b) !== null; $b++) { - $itemlist = $documentlist->item($b)->getElementsByTagName('item'); - $ilslink=''; - if ($documentlist->item($b)->attributes->getNamedItem('href')!==null) { - $ilslink = $documentlist->item($b)->attributes - ->getNamedItem('href')->nodeValue; - } - $emptyResult = array( - 'callnumber' => '-', - 'availability' => '0', - 'number' => 1, - 'reserve' => 'No', - 'duedate' => '', - 'queue' => '', - 'delay' => '', - 'barcode' => 'No samples', - 'status' => '', - 'id' => $id, - 'location' => '', - 'ilslink' => $ilslink, - 'label' => 'No samples' - ); - for ($c = 0; $itemlist->item($c) !== null; $c++) { - $result = array( - 'callnumber' => '', - 'availability' => '0', - 'number' => ($c+1), - 'reserve' => 'No', - 'duedate' => '', - 'queue' => '', - 'delay' => '', - 'barcode' => 1, - 'status' => '', - 'id' => $id, - 'item_id' => '', - 'recallhref' => '', - 'location' => '', - 'location.id' => '', - 'location.href' => '', - 'label' => '', - 'notes' => array() - ); - if ($itemlist->item($c)->attributes->getNamedItem('id') !== null) { - $result['item_id'] = $itemlist->item($c)->attributes - ->getNamedItem('id')->nodeValue; - } - if ($itemlist->item($c)->attributes->getNamedItem('href') !== null) { - $result['recallhref'] = $itemlist->item($c)->attributes - ->getNamedItem('href')->nodeValue; - } - $departmentElements = $itemlist->item($c) - ->getElementsByTagName('department'); - if ($departmentElements->length > 0) { - if ($departmentElements->item(0)->nodeValue) { - $result['location'] - = $departmentElements->item(0)->nodeValue; - $result['location.id'] = $departmentElements - ->item(0)->attributes->getNamedItem('id')->nodeValue; - $result['location.href'] = $departmentElements - ->item(0)->attributes->getNamedItem('href')->nodeValue; - } - } - $storageElements - = $itemlist->item($c)->getElementsByTagName('storage'); - if ($storageElements->length > 0) { - if ($storageElements->item(0)->nodeValue) { - $result['location'] = $storageElements->item(0)->nodeValue; - //$result['location.id'] = $storageElements->item(0) - // ->attributes->getNamedItem('id')->nodeValue; - $result['location.href'] = $storageElements->item(0) - ->attributes->getNamedItem('href')->nodeValue; - //$result['barcode'] = $result['location.id']; - } - } - $barcodeElements - = $itemlist->item($c)->getElementsByTagName('identifier'); - if ($barcodeElements->length > 0) { - if ($barcodeElements->item(0)->nodeValue) { - $result['barcode'] = $barcodeElements->item(0)->nodeValue; - } - } - $labelElements = $itemlist->item($c)->getElementsByTagName('label'); - if ($labelElements->length > 0) { - if ($labelElements->item(0)->nodeValue) { - $result['label'] = $labelElements->item(0)->nodeValue; - $result['callnumber'] - = urldecode($labelElements->item(0)->nodeValue); - } - } - $messageElements - = $itemlist->item($c)->getElementsByTagName('message'); - if ($messageElements->length > 0) { - for ($m = 0; $messageElements->item($m) !== null; $m++) { - $errno = $messageElements->item($m)->attributes - ->getNamedItem('errno')->nodeValue; - if ($errno === '404') { - $result['status'] = 'missing'; - } else if ($this->logger) { - $lang = $messageElements->item($m)->attributes - ->getNamedItem('lang')->nodeValue; - $logString = "[DAIA] message for {$lang}: " - . $messageElements->item($m)->nodeValue; - $this->debug($logString); - } - } - } - - //$loanAvail = 0; - //$loanExp = 0; - //$presAvail = 0; - //$presExp = 0; - - $unavailableElements = $itemlist->item($c) - ->getElementsByTagName('unavailable'); - if ($unavailableElements->item(0) !== null) { - for ($n = 0; $unavailableElements->item($n) !== null; $n++) { - $service = $unavailableElements->item($n)->attributes - ->getNamedItem('service'); - $expectedNode = $unavailableElements->item($n)->attributes - ->getNamedItem('expected'); - $queueNode = $unavailableElements->item($n)->attributes - ->getNamedItem('queue'); - if ($service !== null) { - $service = $service->nodeValue; - if ($service === 'presentation') { - $result['presentation.availability'] = '0'; - $result['presentation_availability'] = '0'; - if ($expectedNode !== null) { - $result['presentation.duedate'] - = $expectedNode->nodeValue; - } - if ($queueNode !== null) { - $result['presentation.queue'] - = $queueNode->nodeValue; - } - $result['availability'] = '0'; - } elseif ($service === 'loan') { - $result['loan.availability'] = '0'; - $result['loan_availability'] = '0'; - if ($expectedNode !== null) { - $result['loan.duedate'] = $expectedNode->nodeValue; - } - if ($queueNode !== null) { - $result['loan.queue'] = $queueNode->nodeValue; - } - $result['availability'] = '0'; - } elseif ($service === 'interloan') { - $result['interloan.availability'] = '0'; - if ($expectedNode !== null) { - $result['interloan.duedate'] - = $expectedNode->nodeValue; - } - if ($queueNode !== null) { - $result['interloan.queue'] = $queueNode->nodeValue; - } - $result['availability'] = '0'; - } elseif ($service === 'openaccess') { - $result['openaccess.availability'] = '0'; - if ($expectedNode !== null) { - $result['openaccess.duedate'] - = $expectedNode->nodeValue; - } - if ($queueNode !== null) { - $result['openaccess.queue'] = $queueNode->nodeValue; - } - $result['availability'] = '0'; - } - } - // TODO: message/limitation - if ($expectedNode !== null) { - $result['duedate'] = $expectedNode->nodeValue; - } - if ($queueNode !== null) { - $result['queue'] = $queueNode->nodeValue; - } - } - } - - $availableElements = $itemlist->item($c) - ->getElementsByTagName('available'); - if ($availableElements->item(0) !== null) { - for ($n = 0; $availableElements->item($n) !== null; $n++) { - $service = $availableElements->item($n)->attributes - ->getNamedItem('service'); - $delayNode = $availableElements->item($n)->attributes - ->getNamedItem('delay'); - if ($service !== null) { - $service = $service->nodeValue; - if ($service === 'presentation') { - $result['presentation.availability'] = '1'; - $result['presentation_availability'] = '1'; - if ($delayNode !== null) { - $result['presentation.delay'] - = $delayNode->nodeValue; - } - $result['availability'] = '1'; - } elseif ($service === 'loan') { - $result['loan.availability'] = '1'; - $result['loan_availability'] = '1'; - if ($delayNode !== null) { - $result['loan.delay'] = $delayNode->nodeValue; - } - $result['availability'] = '1'; - } elseif ($service === 'interloan') { - $result['interloan.availability'] = '1'; - if ($delayNode !== null) { - $result['interloan.delay'] = $delayNode->nodeValue; - } - $result['availability'] = '1'; - } elseif ($service === 'openaccess') { - $result['openaccess.availability'] = '1'; - if ($delayNode !== null) { - $result['openaccess.delay'] = $delayNode->nodeValue; - } - $result['availability'] = '1'; - } - } - // TODO: message/limitation - if ($delayNode !== null) { - $result['delay'] = $delayNode->nodeValue; - } - } - } - // document has no availability elements, so set availability - // and barcode to -1 - if ($availableElements->item(0) === null - && $unavailableElements->item(0) === null - ) { - $result['availability'] = '-1'; - $result['barcode'] = '-1'; - } - $result['ilslink'] = $ilslink; - $status[] = $result; - /* $status = "available"; - if (loanAvail) return 0; - if (presAvail) { - if (loanExp) return 1; - return 2; - } - if (loanExp) return 3; - if (presExp) return 4; - return 5; - */ - } - if (count($status) === 0) { - $status[] = $emptyResult; - } } - return $status; + return $id; } } \ No newline at end of file diff --git a/module/finc/src/finc/ILS/Driver/PAIA.php b/module/finc/src/finc/ILS/Driver/PAIA.php index 3a7bee9b3bb5ac49267b6679663f5f0a755d7c43..b504153eae2ef24d43130b05f561d1e3f54fce7e 100644 --- a/module/finc/src/finc/ILS/Driver/PAIA.php +++ b/module/finc/src/finc/ILS/Driver/PAIA.php @@ -54,13 +54,9 @@ class PAIA extends DAIA { private $_username; private $_password; - private $_ldapConfigurationParameter; protected $baseURL; protected $paiaURL; - protected $catalogURL; - protected $loanURL; - protected $opacfno; /** * Constructor @@ -71,19 +67,11 @@ class PAIA extends DAIA { parent::init(); - if (!isset($this->config['Catalog']['URL'])) { - throw new ILSException('Catalog/URL configuration needs to be set.'); + if (!(isset($this->config['PAIA']['baseUrl']))) { + throw new ILSException('PAIA/URL configuration needs to be set.'); } - if (!(isset($this->config['PAIA']['URL']) || isset($this->config['Catalog']['loanURL']))) { - throw new ILSException('Catalog/loanURL or PAIA/URL configuration needs to be set.'); - } - - $this->catalogURL = $this->config['Catalog']['URL']; - $this->loanURL = $this->config['Catalog']['loanURL']; - $this->opacfno = $this->config['Catalog']['opacfno']; - - $this->paiaURL = $this->config['PAIA']['URL']; + $this->paiaURL = $this->config['PAIA']['baseUrl']; } @@ -91,7 +79,7 @@ class PAIA extends DAIA /* - cancelHolds + cancelHolds X checkRequestIsValid findReserves getCancelHoldDetails @@ -132,23 +120,23 @@ class PAIA extends DAIA * * This is responsible for authenticating a patron against the catalog. * - * @param string $barcode The patron barcode - * @param string $login The patron's last name or PIN (depending on config) + * @param string $username The patron's username + * @param string $password The patron's login password * * @throws ILSException * @return mixed Associative array of patron info on successful login, * null on unsuccessful login. */ - public function patronLogin($barcode, $password) + public function patronLogin($username, $password) { - if ($barcode == '' || $password == '') { + if ($username == '' || $password == '') { return new PEAR_Error('Invalid Login, Please try again.'); } - $this->_username = $barcode; + $this->_username = $username; $this->_password = $password; try { - return $this->_paiaLogin($barcode, $password); + return $this->_paiaLogin($username, $password); } catch (ILSException $e) { throw new ILSException($e->getMessage()); } @@ -198,23 +186,29 @@ class PAIA extends DAIA $loans_response = $this->_getAsArray('/core/'.$patron['cat_username'].'/items'); $holds = count($loans_response['doc']); for ($i = 0; $i < $holds; $i++) { - if ($loans_response['doc'][$i]['status'] == '3') { + if ($loans_response['doc'][$i]['status'] == '3') { //status: held (the document is on loan by the patron) // TODO: set renewable dynamically (not yet supported by PAIA) $renewable = true; $renew_details = $loans_response['doc'][$i]['item']; - /* if ($loans_response['doc'][$i]['cancancel'] == 1) { - $renewable = true; - $renew_details = $loans_response['doc'][$i]['item']; - } */ - // get PPN from PICA catalog since it is not part of PAIA - $ppn = $this->_getPpnByBarcode($loans_response['doc'][$i]['label']); - if ($loans_response['doc'][$i]['status'] == '4') { + /* + * if ($loans_response['doc'][$i]['cancancel'] == 1) { + * $renewable = true; + * $renew_details = $loans_response['doc'][$i]['item']; + * } */ + + // hook for retrieving alternative ItemId in case PAIA does not + // the needed id + $alternativeItemId = $this->_getAlternativeItemId($loans_response['doc'][$i]['item']); + + if ($loans_response['doc'][$i]['status'] == '4') { //status: provided (the document is ready to be used by the patron) $message = "hold_available"; } $transList[] = array( - 'id' => $ppn ? $ppn : $loans_response['doc'][$i]['item'], + 'id' => $alternativeItemId ? $alternativeItemId : $loans_response['doc'][$i]['item'], 'duedate' => $loans_response['doc'][$i]['endtime'], + 'dueTime' => null, + 'dueStatus' => null, 'barcode' => $loans_response['doc'][$i]['item'], 'renew' => $loans_response['doc'][$i]['renewals'], 'renewLimit' => "1", @@ -223,26 +217,41 @@ class PAIA extends DAIA 'publication_year' => null, 'renewable' => $renewable, 'renew_details' => $renew_details, - 'message' => $loans_response['doc'][$i]['label'], + 'message' => $message ? $message : $loans_response['doc'][$i]['label'], 'title' => $loans_response['doc'][$i]['about'], 'item_id' => $loans_response['doc'][$i]['item'], 'institution_name' => null, - 'callnumber' => $loans_response['doc'][$i]['label'], - 'location' => $loans_response['doc'][$i]['storage'], + 'isbn' => null, + 'issn' => null, + 'oclc' => null, + 'upc' => null, + 'callnumber' => $loans_response['doc'][$i]['label'], //non-standard + 'borrowingLocation' => $loans_response['doc'][$i]['storage'], ); } } - //print_r($transList); return $transList; } /** - * Renew item(s) - * - * @param string $recordId Record identifier - * - * @return bool True on success - * @access public + * This method renews a list of items for a specific patron. + * + * @param array $details - An associative array with two keys: + * patron - array returned by patronLogin method + * details - array of values returned by the getRenewDetails method + * identifying which items to renew + * + * @return array - An associative array with two keys: + * blocks - An array of strings specifying why a user is blocked from + * renewing (false if no blocks) + * details - Not set when blocks exist; otherwise, an array of + * associative arrays (keyed by item ID) with each subarray + * containing these keys: + * success – Boolean true or false + * new_date – string – A new due date + * new_time – string – A new due time + * item_id – The item id of the renewed item + * sysMessage – A system supplied renewal message (optional) */ public function renewMyItems($details) { @@ -265,13 +274,26 @@ class PAIA extends DAIA foreach ($elements as $element) { $item_id = $element['item']; if (array_key_exists('error', $element)) { - $details[$item_id] = array('success' => false, 'sysMessage' => $element['error']); + $details[$item_id] = array( + 'success' => false, + 'sysMessage' => $element['error'] + ); } elseif ($element['status'] == '3') { - $details[$item_id] = array('success' => true, 'new_date' => $element['endtime'], 'item_id' => 0, 'sysMessage' => 'Successfully renewed'); + $details[$item_id] = array( + 'success' => true, + 'new_date' => $element['endtime'], + 'item_id' => 0, + 'sysMessage' => 'Successfully renewed' + ); } else { - $details[$item_id] = array('success' => false, 'new_date' => $element['endtime'], 'item_id' => 0, 'sysMessage' => 'Request rejected'); + $details[$item_id] = array( + 'success' => false, + 'new_date' => $element['endtime'], + 'item_id' => 0, + 'sysMessage' => 'Request rejected' + ); } } } @@ -279,17 +301,41 @@ class PAIA extends DAIA return $returnArray; } + /** + * This method returns a string to use as the input form value for renewing + * each hold item. (optional, but required if you implement the + * renewMyItems method) Not supported prior to VuFind 1.2 + * + * @param $checkOutDetails - One of the individual item arrays returned by + * the getMyTransactions method + * @return string - A string to use as the input form value for renewing + * each item; you can pass any data that is needed by your + * ILS to identify the transaction to renew – the output + * of this method will be used as part of the input to the + * renewMyItems method. + */ public function getRenewDetails($checkOutDetails) { return($checkOutDetails['renew_details']); } /** - * Cancel item(s) - * - * @param string $recordId Record identifier - * - * @return bool True on success - * @access public + * This method cancels a list of holds for a specific patron. + * + * @param array $cancelDetails - An associative array with two keys: + * patron - array returned by the driver's patronLogin method + * details - an array of strings returned by the driver's + * getCancelHoldDetails method + * + * @return array - Associative array containing: + * count – The number of items successfully cancelled + * items – Associative array where key matches one of the item_id + * values returned by getMyHolds and the value is an + * associative array with these keys: + * success – Boolean true or false + * status – A status message from the language file + * (required – VuFind-specific message, + * subject to translation) + * sysMessage - A system supplied failure message */ public function cancelHolds($cancelDetails) { @@ -305,7 +351,11 @@ class PAIA extends DAIA $details = array(); if (array_key_exists('error', $array_response)) { - $details[] = array('success' => false, 'status' => $array_response['error_description'], 'sysMessage' => $array_response['error']); + $details[] = array( + 'success' => false, + 'status' => $array_response['error_description'], + 'sysMessage' => $array_response['error'] + ); } else { $count = 0; @@ -313,10 +363,18 @@ class PAIA extends DAIA foreach ($elements as $element) { $item_id = $element['item']; if ($element['error']) { - $details[$item_id] = array('success' => false, 'status' => $element['error'], 'sysMessage' => 'Cancel request rejected'); + $details[$item_id] = array( + 'success' => false, + 'status' => $element['error'], + 'sysMessage' => 'Cancel request rejected' + ); } else { - $details[$item_id] = array('success' => true, 'status' => 'Success', 'sysMessage' => 'Successfully cancelled'); + $details[$item_id] = array( + 'success' => true, + 'status' => 'Success', + 'sysMessage' => 'Successfully cancelled' + ); $count++; } } @@ -326,6 +384,19 @@ class PAIA extends DAIA return $returnArray; } + /** + * This method returns a string to use as the input form value for + * cancelling each hold item. (optional, but required if you + * implement cancelHolds). Not supported prior to VuFind 1.2 + * + * @param $checkOutDetails - One of the individual item arrays returned by + * the getMyHolds method + * @return string - A string to use as the input form value for cancelling + * each hold item; you can pass any data that is needed + * by your ILS to identify the hold – the output of this + * method will be used as part of the input to the + * cancelHolds method. + */ public function getCancelHoldDetails($checkOutDetails) { return($checkOutDetails['cancel_details']); } @@ -343,24 +414,21 @@ class PAIA extends DAIA */ public function getMyFines($patron) { - $fees_response = $this->_getAsArray('/core/'.$patron['cat_username'].'/fees'); $fineList = array(); foreach ($fees_response['fee'] as $fine) { - $ppn = $this->_getPpnByBarcode(substr($fine['item'], -8)); + $alternativeItemId = $this->_getAlternativeItemId($fine['item']); $fineList[] = array( - "id" => $ppn, + "id" => $alternativeItemId ? $alternativeItemId : $fine['item'], "amount" => $fine['amount'], "checkout" => "", "title" => $fine['about'], - "feedate" => $fine['date'], + "createdate" => $fine['date'], "duedate" => "", - "fine" => $fine['feetype'] + "fine" => $fine['feetype'], + //"balance" => "", ); - // id should be the ppn of the book resulting the fine but there's - // currently no way to find out the PPN (we have neither barcode nor - // signature...) } $fineList[] = array( "balance" => $fees_response['amount'] @@ -389,7 +457,7 @@ class PAIA extends DAIA // this is not yet supported by PAIA if ($loans_response['doc'][$i]['status'] == '1' || $loans_response['doc'][$i]['status'] == '2') { // get PPN from PICA catalog since it is not part of PAIA - $ppn = $this->_getPpnByBarcode($loans_response['doc'][$i]['label']); + $alternativeItemId = $this->_getAlternativeItemId($loans_response['doc'][$i]['label']); $cancel_details = false; if ($loans_response['doc'][$i]['cancancel'] == 1) { $cancel_details = $loans_response['doc'][$i]['item']; @@ -399,7 +467,7 @@ class PAIA extends DAIA $transList[] = array( 'type' => $loans_response['doc'][$i]['status'], - 'id' => $ppn ? $ppn : $loans_response['doc'][$i]['item'], + 'id' => $alternativeItemId ? $alternativeItemId : $loans_response['doc'][$i]['item'], 'location' => $loans_response['doc'][$i]['storage'], 'reqnum' => null, 'expire' => isset($loans_response['doc'][$i]['endtime']) ? $loans_response['doc'][$i]['endtime'] : "", @@ -410,13 +478,16 @@ class PAIA extends DAIA 'volume' => null, 'publication_year' => null, 'title' => $loans_response['doc'][$i]['about'], + 'isbn' => null, + 'issn' => null, + 'oclc' => null, + 'upc' => null, 'message' => $loans_response['doc'][$i]['label'], 'callnumber' => $loans_response['doc'][$i]['label'], 'cancel_details' => $cancel_details, ); } } - //print_r($transList); return $transList; } @@ -447,67 +518,135 @@ class PAIA extends DAIA $details = array(); if (array_key_exists('error', $array_response)) { - $details = array('success' => false, 'sysMessage' => $array_response['error_description']); + $details = array( + 'success' => false, + 'sysMessage' => $array_response['error_description'] + ); } else { $elements = $array_response['doc']; foreach ($elements as $element) { if (array_key_exists('error', $element)) { - $details = array('success' => false, 'sysMessage' => $element['error']); + $details = array( + 'success' => false, + 'sysMessage' => $element['error'] + ); } else { - $details = array('success' => true, 'sysMessage' => 'Successfully requested'); + $details = array( + 'success' => true, + 'sysMessage' => 'Successfully requested' + ); } } } - $returnArray = $details; - return $returnArray; + return $details; } /** - * Get Hold Link + * Get Funds * - * The goal for this method is to return a URL to a "place hold" web page on - * the ILS OPAC. This is used for ILSs that do not support an API or method - * to place Holds. + * Return a list of funds which may be used to limit the getNewItems list. + * + * @return array An associative array with key = fund ID, value = fund name. + * @access public + */ + public function getFunds() + { + // If you do not want or support such limits, just return an empty + // array here and the limit control on the new item search screen + // will disappear. + return array(); + } + + /** + * Public Function which changes the password in the library system + * (not supported prior to VuFind 2.4) * - * @param string $id The id of the bib record - * @param array $details Item details from getHoldings return array + * @param string $function The name of the feature to be checked * - * @return string URL to ILS's OPAC's place hold screen. + * @return array An array with patron information. + * @access public */ - public function getHoldLink($id, $details) + public function changePassword($patron, $oldPassword, $newPassword) { - if (isset($details['item_id'])) { - $epn = $details['item_id']; - if (preg_match("/epn:([X\d]{9})/", $epn, $match)) { - $epn = $match[1]; + $post_data = array( + "patron" => $patron['username'], + "username" => $patron['firstname']." ".$patron['lastname'], + "old_password" => $oldPassword, + "new_password" => $newPassword); + + $array_response = $this->_postAsArray('/auth/change', $post_data); + + $details = array(); + + if (array_key_exists('error', $array_response)) { + $details = array( + 'success' => false, + 'status' => $array_response['error'], + 'sysMessage' => $array_response['error_description'] + ); + } + else { + $element = $array_response['patron']; + if (array_key_exists('error', $element)) { + $details = array( + 'success' => false, + 'status' => 'Failure changing password', + 'sysMessage' => $element['error'] + ); + } + else { + $details = array( + 'success' => true, + 'status' => 'Successfully changed' + ); } - $hold = $this->loanURL."?EPN=". $this->prfz($epn) . "&MTR=mon" - ."&BES=".$this->opacfno."&LOGIN=ANONYMOUS"; - /* $hold = $this->loanURL."?MTR=mon&LOGIN=ANONYMOUS" - ."&BES=".$this->opacfno - ."&EPN=".$epn; */ - return $hold; } - return $this->opcloan."?MTR=mon" ."&BES=".$this->opacfno - ."&EPN=".$id; + return $details; } - /** - * Get Funds + * Get Pick Up Locations * - * Return a list of funds which may be used to limit the getNewItems list. + * This is responsible for gettting a list of valid library locations for + * holds / recall retrieval * - * TODO: implement it for PICA + * @param array $patron Patron information returned by the patronLogin + * method. + * @param array $holdDetails Optional array, only passed in when getting a list + * in the context of placing a hold; contains most of the same values passed to + * placeHold, minus the patron data. May be used to limit the pickup options + * or may be ignored. The driver must not add new options to the return array + * based on this data or other areas of VuFind may behave incorrectly. * - * @return array An associative array with key = fund ID, value = fund name. - * @access public + * @throws ILSException + * @return array An array of associative arrays with locationID and + * locationDisplay keys + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getFunds() + public function getPickUpLocations($patron = false, $holdDetails = null) + { + // How to get valid PickupLocations for a PICA LBS? + return array(); + } + + /** + * Get Default Pick Up Location + * + * @param array $patron Patron information returned by the patronLogin + * method. + * @param array $holdDetails Optional array, only passed in when getting a list + * in the context of placing a hold; contains most of the same values passed to + * placeHold, minus the patron data. May be used to limit the pickup options + * or may be ignored. + * + * @return string The default pickup location for the patron. + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getDefaultPickUpLocation($patron = false, $holdDetails = null) { - return null; + return false; } @@ -546,7 +685,6 @@ class PAIA extends DAIA private function _getit($file, $access_token) { - $http = curl_init(); curl_setopt($http, CURLOPT_URL, $this->paiaURL . $file); curl_setopt($http, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' .$access_token, 'Content-type: application/json; charset=UTF-8')); @@ -556,8 +694,8 @@ class PAIA extends DAIA return $data; } - private function _getAsArray($file) { - + private function _getAsArray($file) + { $pure_response = $this->_getit($file, $_SESSION['paiaToken']); $json_start = strpos($pure_response, '{'); $json_response = substr($pure_response, $json_start); @@ -565,19 +703,16 @@ class PAIA extends DAIA // if the login auth token is invalid, renew it (this is possible unless the session is expired) if (isset($loans_response['error']) && $loans_response['code'] == '401') { - $sessionuser = $_SESSION['picauser']; - $this->_paiaLogin($sessionuser->username, $sessionuser->cat_password); - - $pure_response = $this->_getit($file, $_SESSION['paiaToken']); - $json_start = strpos($pure_response, '{'); - $json_response = substr($pure_response, $json_start); - $loans_response = json_decode($json_response, true); + //TODO: handling of expired auth token + $this->debug("Auth token invalid - returning empty array"); + return array(); } return $loans_response; } - private function _postAsArray($file, $data) { + private function _postAsArray($file, $data) + { $pure_response = $this->_postit($file, $data, $_SESSION['paiaToken']); $json_start = strpos($pure_response, '{'); $json_response = substr($pure_response, $json_start); @@ -585,68 +720,14 @@ class PAIA extends DAIA // if the login auth token is invalid, renew it (this is possible unless the session is expired) if ($loans_response['error'] && $loans_response['code'] == '401') { - $sessionuser = $_SESSION['picauser']; - $this->_paiaLogin($sessionuser->username, $sessionuser->cat_password); - - $pure_response = $this->_postit($file, $data, $_SESSION['paiaToken']); - $json_start = strpos($pure_response, '{'); - $json_response = substr($pure_response, $json_start); - $loans_response = json_decode($json_response, true); + //TODO: handling of expired auth token + $this->debug("Auth token invalid - returning empty array"); + return array(); } return $loans_response; } - /** - * gets a PPN by its barcode - * - * @param string $barcode Barcode to use for lookup - * - * @return string PPN - * @access private - */ - private function _getPpnByBarcode($barcode) - { - $barcode = str_replace("/"," ",$barcode); - $searchUrl = $this->catalogURL . - "XML=1.0/CMD?ACT=SRCHA&IKT=1016&SRT=YOP&TRM=sgn+$barcode"; - - $doc = new DomDocument(); - $doc->load($searchUrl); - // get Availability information from DAIA - $itemlist = $doc->getElementsByTagName('SHORTTITLE'); - if (isset($itemlist->item(0)->attributes) && count($itemlist->item(0)->attributes) > 0) { - $ppn = $itemlist->item(0)->attributes->getNamedItem('PPN')->nodeValue; - } else { - return false; - } - return $ppn; - } - - /** - * gets holdings of magazine and journal exemplars - * - * @param string $ppn PPN identifier - * - * @return array - * @access public - */ - public function getJournalHoldings($ppn) - { - $searchUrl = $this->catalogURL . - "XML=1.0/SET=1/TTL=1/FAM?PPN=" . $ppn . "&SHRTST=100"; - $doc = new DomDocument(); - $doc->load($searchUrl); - $itemlist = $doc->getElementsByTagName('SHORTTITLE'); - $ppn = array(); - for ($n = 0; $itemlist->item($n); $n++) { - if (count($itemlist->item($n)->attributes) > 0) { - $ppn[] = $itemlist->item($n)->attributes->getNamedItem('PPN')->nodeValue; - } - } - return $ppn; - } - /** * private authentication function * use PAIA for authentication @@ -657,7 +738,12 @@ class PAIA extends DAIA */ private function _paiaLogin($username, $password) { - $post_data = array("username" => $username, "password" => $password, "grant_type" => "password", "scope" => "read_patron read_fees read_items write_items change_password"); + $post_data = array( + "username" => $username, + "password" => $password, + "grant_type" => "password", + "scope" => "read_patron read_fees read_items write_items change_password" + ); $login_response = $this->_postit('/auth/login', $post_data); $json_start = strpos($login_response, '{'); @@ -667,10 +753,10 @@ class PAIA extends DAIA if (array_key_exists('access_token', $array_response)) { $_SESSION['paiaToken'] = $array_response['access_token']; if (array_key_exists('patron', $array_response)) { - $user = $this->_getUserDetails($array_response['patron']); - $user['cat_username'] = $array_response['patron']; - $user['cat_password'] = $password; - return $user; + $patron = $this->_getUserDetails($array_response['patron']); + $patron['cat_username'] = $array_response['patron']; + $patron['cat_password'] = $password; + return $patron; } else { throw new ILSException('Login credentials accepted, but got no patron ID?!?'); @@ -700,12 +786,9 @@ class PAIA extends DAIA // if the login auth token is invalid, renew it (this is possible unless the session is expired) if (isset($user_response['error']) && $user_response['code'] == '401') { - $this->_paiaLogin($sessionuser->username, $sessionuser->cat_password); - - $pure_response = $this->_getit('/core/'.$data, $_SESSION['paiaToken']); - $json_start = strpos($pure_response, '{'); - $json_response = substr($pure_response, $json_start); - $user_response = json_decode($json_response, true); + //TODO: handling of expired auth token + $this->debug("Auth token invalid - returning empty userdetails"); + return array(); } $username = $user_response['name']; @@ -721,15 +804,22 @@ class PAIA extends DAIA $user['major'] = null; $user['college'] = null; - // do not store cat_password into database, but assign it to Session user - /* - $sessionuser = new User(); - $sessionuser->username = $this->_username; - $sessionuser->cat_password = $this->_password; - */ return $user; } + /** + * Support method to retrieve needed ItemId in case PAIA-resposne does not + * contain it + * + * @param string $id itemId + * + * @return string $id + * @access private + */ + private function _getAlternativeItemId($id) { + return $id; + } + /** * Public Function which retrieves renew, hold and cancel settings from the * driver ini file. @@ -748,123 +838,5 @@ class PAIA extends DAIA } return $functionConfig; } - - /** - * Public Function which changes the password in the library system - * - * @param string $function The name of the feature to be checked - * - * @return array An array with patron information. - * @access public - */ - public function changePassword($patron, $newpassword) - { - $sessionuser = $_SESSION['picauser']; - - $post_data = array("patron" => $patron['username'], - "username" => $patron['firstname']." ".$patron['lastname'], - "old_password" => $sessionuser->cat_password, - "new_password" => $newpassword); - - $array_response = $this->_postAsArray('/auth/change', $post_data); - - $details = array(); - - if (array_key_exists('error', $array_response)) { - $details = array('success' => false, 'status' => $array_response['error'], 'sysMessage' => $array_response['error_description']); - } - else { - $element = $array_response['patron']; - if (array_key_exists('error', $element)) { - $details = array('success' => false, 'status' => 'Failure changing password', 'sysMessage' => $element['error']); - } - else { - $details = array('success' => true, 'status' => 'Successfully changed'); - - // TODO: push password also to LDAP (but make that configurable since this is non-standard) - - // replace password for currently logged in user with the new one - $sessionuser->password = $newsecret; - $sessionuser->cat_password = $newsecret; - $_SESSION['picauser'] = $sessionuser; - } - } - $returnArray = $details; - return $returnArray; - } - - /** - * Get Pick Up Locations - * - * This is responsible for gettting a list of valid library locations for - * holds / recall retrieval - * - * @param array $patron Patron information returned by the patronLogin - * method. - * @param array $holdDetails Optional array, only passed in when getting a list - * in the context of placing a hold; contains most of the same values passed to - * placeHold, minus the patron data. May be used to limit the pickup options - * or may be ignored. The driver must not add new options to the return array - * based on this data or other areas of VuFind may behave incorrectly. - * - * @throws ILSException - * @return array An array of associative arrays with locationID and - * locationDisplay keys - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getPickUpLocations($patron = false, $holdDetails = null) - { - // How to get valid PickupLocations for a PICA LBS? - return array(); - } - - /** - * Get Default Pick Up Location - * - * @param array $patron Patron information returned by the patronLogin - * method. - * @param array $holdDetails Optional array, only passed in when getting a list - * in the context of placing a hold; contains most of the same values passed to - * placeHold, minus the patron data. May be used to limit the pickup options - * or may be ignored. - * - * @return string The default pickup location for the patron. - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getDefaultPickUpLocation($patron = false, $holdDetails = null) - { - return false; - } - - /** - * Helper function to compute the modulo 11 based - * ppn control number - * - * @param string $str input - * - * @return string - */ - protected function prfz($str) - { - $x = 0; $y = 0; $w = 2; - $stra = str_split($str); - for ($i=strlen($str); $i>0; $i--) { - $c = $stra[$i-1]; - $x = ord($c) - 48; - $y += $x*$w; - $w++; - } - $p = 11-$y%11; - if ($p==11) { - $p=0; - } - if ($p==10) { - $ret = $str."X"; - } else { - $ret = $str.$p; - } - return $ret; - } - } ?> \ No newline at end of file