From dcca27a49784cbd326bfee9d009e6657444ee753 Mon Sep 17 00:00:00 2001 From: Gregor Gawol <gawol@ub.uni-leipzig.de> Date: Mon, 26 Jan 2015 15:33:38 +0100 Subject: [PATCH] refs #4533: * implemented full finc specific RecordDriver stack including most of functionality of VuFind1.x finc-RecordDrivers * extended config.ini by several finc specific settings (cf. finc block at the bottom of config.ini) --- .../config/vufind/SolrMarcRemoteFinc.ini | 22 + local/config/vufind/SolrMarcRemoteFinc.ini | 3 + local/config/vufind/config.ini | 20 + .../vufind/SolrMarcRemoteFinc.ini.sample | 22 + module/finc/config/module.config.php | 36 +- module/finc/src/finc/RecordDriver/Factory.php | 22 + .../src/finc/RecordDriver/SolrDefault.php | 520 ++++++++ .../finc/src/finc/RecordDriver/SolrMarc.php | 1138 +++++++++++++++++ .../src/finc/RecordDriver/SolrMarcRemote.php | 20 +- .../finc/RecordDriver/SolrMarcRemoteFinc.php | 1020 +++++++++++++++ 10 files changed, 2818 insertions(+), 5 deletions(-) create mode 100644 local/alpha/config/vufind/SolrMarcRemoteFinc.ini create mode 100644 local/config/vufind/SolrMarcRemoteFinc.ini create mode 100644 local/dev/config/vufind/SolrMarcRemoteFinc.ini.sample create mode 100644 module/finc/src/finc/RecordDriver/SolrDefault.php create mode 100644 module/finc/src/finc/RecordDriver/SolrMarc.php create mode 100644 module/finc/src/finc/RecordDriver/SolrMarcRemoteFinc.php diff --git a/local/alpha/config/vufind/SolrMarcRemoteFinc.ini b/local/alpha/config/vufind/SolrMarcRemoteFinc.ini new file mode 100644 index 00000000000..f5359f864a5 --- /dev/null +++ b/local/alpha/config/vufind/SolrMarcRemoteFinc.ini @@ -0,0 +1,22 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2015 ############## +; +; This is the default ALPHA-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 = ../../../config/vufind/SolrMarcRemoteFinc.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 = "Languages,AlphaBrowse_Types" + +; +; Add ALPHA-specific customization after this header. +; +;##################### DO NOT DELETE THIS HEADER #################### +;#################################################################### diff --git a/local/config/vufind/SolrMarcRemoteFinc.ini b/local/config/vufind/SolrMarcRemoteFinc.ini new file mode 100644 index 00000000000..81cb438500f --- /dev/null +++ b/local/config/vufind/SolrMarcRemoteFinc.ini @@ -0,0 +1,3 @@ +[General] +; Set the URI-pattern of the server which serves the raw Marc-data. +baseUrl = http://172.18.113.147/%s \ No newline at end of file diff --git a/local/config/vufind/config.ini b/local/config/vufind/config.ini index 7ccc30fb3a6..a9472b017b7 100644 --- a/local/config/vufind/config.ini +++ b/local/config/vufind/config.ini @@ -1145,6 +1145,26 @@ max_tag_length = 64 ;sigel = "15" ;isil = "DE-15" ;bik = "952000-4" + +; This section defines libraries forms a group. +;[LibraryGroup] +;libraries = DE-15,DE-15-292,DE-15-100 + +; This section contains all site related customization for finc +;[CustomSite] +; Combine more formats to one css class. If false first format entry will taken +; to display icon symbol +;combinedIcons = false +;namespace = ubl + +; This section contains all index related customizations for finc +;[CustomIndex] +; Special settings to control single instances of libaries within one vufind +; installation. +;indexExtension = "de15" ; for solr index of hmt +; take general format field of Solr index. If false it takes the format fields +; with index extension defined above. +;generalFormats = true ;for ubl & htwk it should be true ; ***************** ; * EOF finc ; ***************** diff --git a/local/dev/config/vufind/SolrMarcRemoteFinc.ini.sample b/local/dev/config/vufind/SolrMarcRemoteFinc.ini.sample new file mode 100644 index 00000000000..7a04483f3cc --- /dev/null +++ b/local/dev/config/vufind/SolrMarcRemoteFinc.ini.sample @@ -0,0 +1,22 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2015 ############## +; +; This is the default DEV-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 = ../../../config/vufind/SolrMarcRemoteFinc.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 = "Languages,AlphaBrowse_Types" + +; +; Add DEV-specific customization after this header. +; +;##################### DO NOT DELETE THIS HEADER #################### +;#################################################################### diff --git a/module/finc/config/module.config.php b/module/finc/config/module.config.php index 6f7408c4033..aa912188218 100644 --- a/module/finc/config/module.config.php +++ b/module/finc/config/module.config.php @@ -15,11 +15,34 @@ $config = array( ), 'recorddriver' => array( 'factories' => array( - 'solrmarcremote' => 'finc\RecordDriver\Factory::getSolrMarcRemote' + 'solrdefault' => 'finc\RecordDriver\Factory::getSolrDefault', + 'solrmarc' => 'finc\RecordDriver\Factory::getSolrMarc', + 'solrmarcremote' => 'finc\RecordDriver\Factory::getSolrMarcRemote', + 'solrmarcremotefinc' => 'finc\RecordDriver\Factory::getSolrMarcRemoteFinc' ), ), ), 'recorddriver_tabs' => array( + 'finc\RecordDriver\SolrDefault' => array( + 'tabs' => array ( + 'Holdings' => 'HoldingsILS', 'Description' => 'Description', + 'TOC' => 'TOC', 'UserComments' => 'UserComments', + 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', + 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', + 'Details' => 'StaffViewArray', + ), + 'defaultTab' => null, + ), + 'finc\RecordDriver\SolrMarc' => array( + 'tabs' => array( + 'Holdings' => 'HoldingsILS', 'Description' => 'Description', + 'TOC' => 'TOC', 'UserComments' => 'UserComments', + 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', + 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', + 'Details' => 'StaffViewMARC', + ), + 'defaultTab' => null, + ), 'finc\RecordDriver\SolrMarcRemote' => array( 'tabs' => array( 'Holdings' => 'HoldingsILS', 'Description' => 'Description', @@ -31,6 +54,17 @@ $config = array( ), 'defaultTab' => null, ), + 'finc\RecordDriver\SolrMarcRemoteFinc' => array( + 'tabs' => array( + 'Holdings' => 'HoldingsILS', 'Description' => 'Description', + 'TOC' => 'TOC', 'UserComments' => 'UserComments', + 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', + 'Preview' => 'preview', + 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', + 'Details' => 'StaffViewMARC', + ), + 'defaultTab' => null, + ), ), ), ); diff --git a/module/finc/src/finc/RecordDriver/Factory.php b/module/finc/src/finc/RecordDriver/Factory.php index 5841f9e27d3..0af75cd058d 100644 --- a/module/finc/src/finc/RecordDriver/Factory.php +++ b/module/finc/src/finc/RecordDriver/Factory.php @@ -62,4 +62,26 @@ class Factory extends \VuFind\RecordDriver\Factory ); return $driver; } + + /** + * Factory for SolrMarcRemoteFinc record driver. + * + * @param ServiceManager $sm Service manager. + * + * @return SolrMarc + */ + public static function getSolrMarcRemoteFinc(ServiceManager $sm) + { + $driver = new SolrMarcRemoteFinc( + $sm->getServiceLocator()->get('VuFind\Config')->get('config'), + $sm->getServiceLocator()->get('VuFind\Config')->get('SolrMarcRemoteFinc'), + $sm->getServiceLocator()->get('VuFind\Config')->get('searches') + ); + $driver->attachILS( + $sm->getServiceLocator()->get('VuFind\ILSConnection'), + $sm->getServiceLocator()->get('VuFind\ILSHoldLogic'), + $sm->getServiceLocator()->get('VuFind\ILSTitleHoldLogic') + ); + return $driver; + } } diff --git a/module/finc/src/finc/RecordDriver/SolrDefault.php b/module/finc/src/finc/RecordDriver/SolrDefault.php new file mode 100644 index 00000000000..0b84d2a28ca --- /dev/null +++ b/module/finc/src/finc/RecordDriver/SolrDefault.php @@ -0,0 +1,520 @@ +<?php +/** + * finc specific model for Solr records based on the stock + * VuFind\RecordDriver\SolrDefault + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @author Frank Morgner <morgnerf@ub.uni-leipzig.de> + * @author Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + * @SuppressWarnings(PHPMD.ExcessivePublicCount) + */ +namespace finc\RecordDriver; + +/** + * finc specific model for Solr records based on the stock + * VuFind\RecordDriver\SolrDefault + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @author Frank Morgner <morgnerf@ub.uni-leipzig.de> + * @author Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + * @SuppressWarnings(PHPMD.ExcessivePublicCount) + */ +class SolrDefault extends \VuFind\RecordDriver\SolrDefault +{ + + /** + * Return the custom index field local_heading if indexExtension is set. + * If indexExtension is set local_heading_{indexExtension} is returned, + * if local_heading_{indexExtesion} is empty, + * local_heading_facet_{indexExtension} is returned. + * + * @return array Containing local_heading_[facet_]{indexExtension} fields. + * @access public + */ + public function getLocalHeading() { + + $array = array(); + + if (isset($this->mainConfig->Site->indexExtension)) { + $array = isset($this->fields['local_heading_' . ($this->mainConfig->Site->indexExtension)]) ? + $this->fields['local_heading_' . ($this->mainConfig->Site->indexExtension)] : array(); + // Use local_heading_facet field if local_heading field delivers no results at first + if (count($array) == 0) { + $array = isset($this->fields['local_heading_facet_' . ($this->mainConfig->Site->indexExtension)]) ? + $this->fields['local_heading_facet_' . ($this->mainConfig->Site->indexExtension)] : array(); + } + } + return $array; + } + + /** + * Controller to decide when local format field of a library should be + * retrieved from marc. Pass through method for PrimoCentral + * + * Public method for tuf to display format at search modul. + * + * @internal This method should be dropped or renamed (getStandardFormat()) + * as it is only a wrapper for the custom method getFormat() which + * in turn behaves as the stock getFormats() method. + * + * @deprecated No need for this wrapper in custom SolrDefault + * + * @return array + * @access public + */ + public function getLocalFormat() + { + return $this->getFormat(); + } + + /** + * Get back the standardizied format field of Solr index. + * + * @deprecated Should also be possible to be dropped (@see getLocalFormat()) + * + * @return array + * @access protected + */ + public function getFormat() + { + return isset($this->fields['format']) ? $this->fields['format'] : array(); + } + + /** + * Get an array of all the formats associated with the record. If indexExtension + * is set and generalFormats is disabled in config.ini return the field + * format_{indexExtension}, format otherwise. + * + * @return array Array with formats associated with the record. + * @access protected + */ + public function getFormats() + { + // check if general 'format' index field should be used + $isGeneralFormat = (isset($this->mainConfig->Site->generalFormats) + && true == $this->mainConfig->Site->generalFormats) + ? true : false; + // check if there's an extension defined for the library depended format + // index field + $isExtension = (isset($this->mainConfig->Site->indexExtension)) + ? true : false; + + $format = (false === $isGeneralFormat && true === $isExtension) + ? 'format_' . $this->mainConfig->Site->indexExtension : 'format' ; + + return isset($this->fields[$format]) ? $this->fields[$format] : array(); + } + + + /** + * Get the formats for displaying the icons. Renders the format information to + * a specific css class. + * There are two setups possible. If combinedIcons sets to true at config.ini + * all format values will be concatenated to one string; if it's false only + * the first vlaue will be taken. + * + * @internal Should be moved out of RecordDriver (Controller/View?) + * @todo Should be moved out of RecordDriver (Controller/View?) + * + * @return array + * @access protected + */ +/* protected function getFormatIcon() + { + global $configArray; + + $format = $this->getFormats(); + // check which method to build the css class is chosen + if (isset($this->mainConfig->Site->combinedIcons) && true == $this->mainConfig->Site->combinedIcons) { + // sort it + sort($format, SORT_LOCALE_STRING); + return strtolower(implode('', $format)); + // otherwise take the first format + } else { + if (isset($this->fields['multipart_set'])) { + switch ($this->fields['multipart_set']) { + case 'a': return 'sets'; + case 'b': break; //return 'part-related'; + case 'c': break; //return 'part-not-related'; + } + } + //echo "<pre>"; print_r($format); echo "</pre>"; + return $format[0]; + } + }*/ + + /** + * Get the source id of the record. + * + * @return string + * @access public + */ + public function getSourceID() + { + return isset($this->fields['source_id']) ? + $this->fields['source_id'] : ''; + } + + /** + * Get the GND of an author. + * + * @return array + * @access protected + */ + protected function getAuthorId() + { + return isset($this->fields['author_id']) ? + $this->fields['author_id'] : array(); + } + + /** + * Combined fields of author data. + * + * @todo Check whether static call of getCorporateAuthor is necessary + * + * @return array + * @access protected + * @link https://intern.finc.info/issues/1866 + */ + protected function getCombinedAuthors() + { + $retval = array(); + + if ($this->getPrimaryAuthor() != '') { + $original = ''; + if ($this->getPrimaryAuthorOrig() != '') { + $original = $this->getPrimaryAuthorOrig(); + } + $retval[] = ($original == '') ? $this->getPrimaryAuthor() + : $this->getPrimaryAuthor() . ' (' . $original . ')'; + } elseif ( self::getCorporateAuthor() != '' ) { + $retval[] = self::getCorporateAuthor(); + } elseif (count($this->getSecondaryAuthors()) > 0) { + foreach ($this->getSecondaryAuthors() as $val) { + $retval[] = $val; + } + } elseif (count($this->getCorporateSecondaryAuthors()) > 0) { + foreach ($this->getCorporateSecondaryAuthors() as $val) { + $retval[] = $val; + } + } + + return $retval; + } + + /** + * Get the original author of the record. + * + * @return string + * @access protected + */ + protected function getPrimaryAuthorOrig() + { + return isset($this->fields['author_orig']) ? + $this->_filterAuthorDates($this->fields['author_orig']) : ''; + } + + /** + * Get the main author of the record. + * + * @return string + * @access protected + * @deprecated + */ + protected function getPrimaryAuthorRaw() + { + return isset($this->fields['author']) ? + $this->_removeAuthorDates($this->fields['author']) : ''; + } + + /** + * Get the main corporate author (if any) for the record. + * + * @return string + * @access public + */ + public function getCorporateAuthor() + { + return isset($this->fields['author_corp']) ? + $this->fields['author_corp'] : ''; + } + + /** + * Get the secondary corporate authors (if any) for the record. + * + * @return array + * @access protected + */ + protected function getCorporateSecondaryAuthors() + { + return isset($this->fields['author_corp2']) ? + $this->fields['author_corp2'] : array(); + } + + /** + * Get an array of all ISMNs associated with the record (may be empty). + * + * @return array + * @access protected + */ + protected function getISMNs() + { + return isset($this->fields['ismn']) && is_array($this->fields['ismn']) ? + $this->fields['ismn'] : array(); + } + + /** + * Get an array of newer titles for the record. + * + * @return array + * @access protected + */ + protected function getNewTitles() + { + return isset($this->fields['title_new']) ? + $this->fields['title_new'] : array(); + } + + /** + * After giving a record ids as e.g. ppn of the BSZ check if a record exists. + * This method can be used to indicate a direct link than to form a general + * look for query. + * + * @todo 1. Check if this method is still needed + * @todo 2. Refactor Solr-Query to be compatible with VuFind2 + * + * @param array $rids Array of record ids to test. + * + * @return int mixed If success return at least one finc id otherwise null. + * @access protected + * @deprecated Not used. + */ + protected function addFincIDToRecord ( $array ) { +/* + // record ids + $rids = array(); + // return array + $retval = array(); + + // check if array contain record_ids and collect it as an array to + // use only one solr request for all + if (isset($array) && is_array($array)) { + foreach ($array as $line) { + if (isset($line['record_id'])) { + $rids[] = $line['record_id']; + } + } + } + // solr call + // call index + $index = $this->getIndexEngine(); + + // build query and accept limit of solr + $limit = $index->getBooleanClauseLimit(); + if (count($rids) > $limit) { + $rids = array_slice($rids, 0, $limit); + $retVal = array(); + } + // build the query: + if (count($rids) == 1) { + // single query: + $query = "(record_id:". $rids[0] .")"; + } elseif (count($rids) > 1) { + // multi query: + $query = 'record_id:(' . implode(' OR ', $rids) . ')'; + } else { + return $array; + } + // set hidden filter to limited the range + $this->setHiddenFilters(); + // limited search for id and record_id values only + $result = $index->search($query, null, $this->hiddenFilters, 0, 100, null, '', null, null, 'id, record_id', HTTP_REQUEST_METHOD_POST , false, false); + + // log to find test data + // temporary logger + if (isset($result['response']['numFound']) + && isset($result['response']['numFound']) != 0) { + } + // if error break down + if (PEAR::isError($result)) { + return null; + } + if (isset($result['response']['docs']) + && !empty($result['response']['docs']) + ) { + foreach( $result['response']['docs'] as $key => $doc) { + $retval[($doc['record_id'])]=$doc['id']; + } + } + // write back in array + foreach ($array as &$val) { + if (isset($val['record_id'])) { + if (isset($retval[($val['record_id'])])) { + $val['id'] = $retval[($val['record_id'])]; + } + } + } + unset($val); + //echo "<pre>"; print_r($array); echo "</pre>";*/ + return $array; + } + + /** + * Get percentage of relevance of a title. First implementaion for TUBAF. + * + * @return float Percentage of Score / Maximum Score rounded by 5. + * @access protected + * @link https://intern.finc.info/issues/1908 + */ + protected function getRelevance() { + + $score = isset($this->fields['score']) ? $this->fields['score'] : 0; + $maxScore = isset($this->fields['score_maximum']) ? $this->fields['score_maximum'] : 0; + + if ($score == 0 || $maxScore == 0) { + return 0; + } + return round( ($score / $maxScore) , 5); + } + + /** + * Get RVK classifcation number from Solr index. + * + * @return string + * @access protected + */ + protected function getRvk() { + return isset($this->fields['rvk_facet']) ? + $this->fields['rvk_facet'] : ''; + } + + /** + * Get special record_id of libero system. + * + * @todo refactor to a more meaningful name? + * + * @return string + * @access protected + */ + public function getRID() + { + return isset($this->fields['record_id']) ? + $this->fields['record_id'] : ''; + } + + /** + * Get the original title of the record. + * + * @return string + * @access protected + */ + protected function getTitleOrig() + { + return isset($this->fields['title_orig']) ? + $this->fields['title_orig'] : ''; + } + + /** + * Get the GND of topic. + * + * @return array + * @access protected + */ + protected function getTopicId() + { + return isset($this->fields['topic_id']) ? + $this->fields['topic_id'] : array(); + } + + /** + * Get alternatives series titles as array. + * + * @return array + * @access protected + */ + protected function getSeriesAlternative() + { + if (isset($this->fields['series2']) && !empty($this->fields['series2'])) { + return $this->fields['series2']; + } + return array(); + } + + /** + * Get alternatives series titles as array. + * + * @return array + * @access protected + */ + protected function getSeriesOrig() + { + if (isset($this->fields['series_orig']) && !empty($this->fields['series_orig'])) { + return $this->fields['series_orig']; + } + return array(); + } + + /** + * Filter author data for author year of birth and death + * to give a better mark up. + * + * @param string authordata + * @return strings + * @access protected + */ + protected function _filterAuthorDates( $authordata ) + { + if (preg_match('/^(\s|.*)(\d{4})\s?-?\s?(\d{4})?$/Uu',$authordata, $match)) { + return (isset($match[3])) + ? $match[1] .' *'. $match[2] . '-†'. $match[3] + : $match[1] .' *'. $match[2] . '-'; + } + return $authordata; + } + + /** + * Remove author dates if exists. + * + * @param string authordata + * + * @return strings + * @access protected + * @deprecated + */ + protected function _removeAuthorDates( $authordata ) + { + if (preg_match('/^(\s|.*)\s(fl.\s|d.\s|ca.\s)*\s?(\d{4})\??(\sor\s\d\d?)?\s?(-|–)?\s?(ca.\s|after\s)?(\d{1,4})?(.|,)?$/Uu',$authordata, $match)) { + return (isset($match[1])) ? $match[1] : $authordata; + } + return $authordata; + } + + +} diff --git a/module/finc/src/finc/RecordDriver/SolrMarc.php b/module/finc/src/finc/RecordDriver/SolrMarc.php new file mode 100644 index 00000000000..747f454800d --- /dev/null +++ b/module/finc/src/finc/RecordDriver/SolrMarc.php @@ -0,0 +1,1138 @@ +<?php +/** + * Model for MARC records in Solr. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +namespace finc\RecordDriver; +use VuFind\Exception\ILS as ILSException, + VuFind\View\Helper\Root\RecordLink, + VuFind\XSLT\Processor as XSLTProcessor; + +/** + * Model for MARC records in Solr. + * + * @category VuFind2 + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +class SolrMarc extends SolrDefault +{ + /** + * MARC record + * + * @var \File_MARC_Record + */ + protected $marcRecord; + + /** + * ILS connection + * + * @var \VuFind\ILS\Connection + */ + protected $ils = null; + + /** + * Hold logic + * + * @var \VuFind\ILS\Logic\Holds + */ + protected $holdLogic; + + /** + * Title hold logic + * + * @var \VuFind\ILS\Logic\TitleHolds + */ + protected $titleHoldLogic; + + /** + * Set raw data to initialize the object. + * + * @param mixed $data Raw data representing the record; Record Model + * objects are normally constructed by Record Driver objects using data + * passed in from a Search Results object. In this case, $data is a Solr record + * array containing MARC data in the 'fullrecord' field. + * + * @return void + */ + public function setRawData($data) + { + // Call the parent's set method... + parent::setRawData($data); + + // Also process the MARC record: + $marc = trim($data['fullrecord']); + + // check if we are dealing with MARCXML + $xmlHead = '<?xml version'; + if (strcasecmp(substr($marc, 0, strlen($xmlHead)), $xmlHead) === 0) { + $marc = new \File_MARCXML($marc, \File_MARCXML::SOURCE_STRING); + } else { + // When indexing over HTTP, SolrMarc may use entities instead of certain + // control characters; we should normalize these: + $marc = str_replace( + array('#29;', '#30;', '#31;'), array("\x1D", "\x1E", "\x1F"), $marc + ); + $marc = new \File_MARC($marc, \File_MARC::SOURCE_STRING); + } + + $this->marcRecord = $marc->next(); + if (!$this->marcRecord) { + throw new \File_MARC_Exception('Cannot Process MARC Record'); + } + } + + /** + * Get access restriction notes for the record. + * + * @return array + */ + public function getAccessRestrictions() + { + return $this->getFieldArray('506'); + } + + /** + * Get all subject headings associated with this record. Each heading is + * returned as an array of chunks, increasing from least specific to most + * specific. + * + * @return array + */ + public function getAllSubjectHeadings() + { + // These are the fields that may contain subject headings: + $fields = array( + '600', '610', '611', '630', '648', '650', '651', '655', '656' + ); + + // This is all the collected data: + $retval = array(); + + // Try each MARC field one at a time: + foreach ($fields as $field) { + // Do we have any results for the current field? If not, try the next. + $results = $this->marcRecord->getFields($field); + if (!$results) { + continue; + } + + // If we got here, we found results -- let's loop through them. + foreach ($results as $result) { + // Start an array for holding the chunks of the current heading: + $current = array(); + + // Get all the chunks and collect them together: + $subfields = $result->getSubfields(); + if ($subfields) { + foreach ($subfields as $subfield) { + // Numeric subfields are for control purposes and should not + // be displayed: + if (!is_numeric($subfield->getCode())) { + $current[] = $subfield->getData(); + } + } + // If we found at least one chunk, add a heading to our result: + if (!empty($current)) { + $retval[] = $current; + } + } + } + } + + // Send back everything we collected: + return $retval; + } + + /** + * Get award notes for the record. + * + * @return array + */ + public function getAwards() + { + return $this->getFieldArray('586'); + } + + /** + * Get the bibliographic level of the current record. + * + * @return string + */ + public function getBibliographicLevel() + { + $leader = $this->marcRecord->getLeader(); + $biblioLevel = strtoupper($leader[7]); + + switch ($biblioLevel) { + case 'M': // Monograph + return "Monograph"; + case 'S': // Serial + return "Serial"; + case 'A': // Monograph Part + return "MonographPart"; + case 'B': // Serial Part + return "SerialPart"; + case 'C': // Collection + return "Collection"; + case 'D': // Collection Part + return "CollectionPart"; + default: + return "Unknown"; + } + } + + /** + * Get notes on bibliography content. + * + * @return array + */ + public function getBibliographyNotes() + { + return $this->getFieldArray('504'); + } + + /** + * Get the main corporate author (if any) for the record. + * + * @return string + */ + public function getCorporateAuthor() + { + // Try 110 first -- if none found, try 710 next. + $main = $this->getFirstFieldValue('110', array('a', 'b')); + if (!empty($main)) { + return $main; + } + return $this->getFirstFieldValue('710', array('a', 'b')); + } + + /** + * Return an array of all values extracted from the specified field/subfield + * combination. If multiple subfields are specified and $concat is true, they + * will be concatenated together in the order listed -- each entry in the array + * will correspond with a single MARC field. If $concat is false, the return + * array will contain separate entries for separate subfields. + * + * @param string $field The MARC field number to read + * @param array $subfields The MARC subfield codes to read + * @param bool $concat Should we concatenate subfields? + * + * @return array + */ + protected function getFieldArray($field, $subfields = null, $concat = true) + { + // Default to subfield a if nothing is specified. + if (!is_array($subfields)) { + $subfields = array('a'); + } + + // Initialize return array + $matches = array(); + + // Try to look up the specified field, return empty array if it doesn't + // exist. + $fields = $this->marcRecord->getFields($field); + if (!is_array($fields)) { + return $matches; + } + + // Extract all the requested subfields, if applicable. + foreach ($fields as $currentField) { + $next = $this->getSubfieldArray($currentField, $subfields, $concat); + $matches = array_merge($matches, $next); + } + + return $matches; + } + + /** + * Get notes on finding aids related to the record. + * + * @return array + */ + public function getFindingAids() + { + return $this->getFieldArray('555'); + } + + /** + * Get the first value matching the specified MARC field and subfields. + * If multiple subfields are specified, they will be concatenated together. + * + * @param string $field The MARC field to read + * @param array $subfields The MARC subfield codes to read + * + * @return string + */ + protected function getFirstFieldValue($field, $subfields = null) + { + $matches = $this->getFieldArray($field, $subfields); + return (is_array($matches) && count($matches) > 0) ? + $matches[0] : null; + } + + /** + * Get general notes on the record. + * + * @return array + */ + public function getGeneralNotes() + { + return $this->getFieldArray('500'); + } + + /** + * Get human readable publication dates for display purposes (may not be suitable + * for computer processing -- use getPublicationDates() for that). + * + * @return array + */ + public function getHumanReadablePublicationDates() + { + return $this->getPublicationInfo('c'); + } + + /** + * Get an array of newer titles for the record. + * + * @return array + */ + public function getNewerTitles() + { + // If the MARC links are being used, return blank array + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? array_map('trim', explode(',', $this->mainConfig->Record->marc_links)) + : array(); + return in_array('785', $fieldsNames) ? array() : parent::getNewerTitles(); + } + + /** + * Get the item's publication information + * + * @param string $subfield The subfield to retrieve ('a' = location, 'c' = date) + * + * @return array + */ + protected function getPublicationInfo($subfield = 'a') + { + // First check old-style 260 field: + $results = $this->getFieldArray('260', array($subfield)); + + // Now track down relevant RDA-style 264 fields; we only care about + // copyright and publication places (and ignore copyright places if + // publication places are present). This behavior is designed to be + // consistent with default SolrMarc handling of names/dates. + $pubResults = $copyResults = array(); + + $fields = $this->marcRecord->getFields('264'); + if (is_array($fields)) { + foreach ($fields as $currentField) { + $currentVal = $currentField->getSubfield($subfield); + $currentVal = is_object($currentVal) + ? $currentVal->getData() : null; + if (!empty($currentVal)) { + switch ($currentField->getIndicator('2')) { + case '1': + $pubResults[] = $currentVal; + break; + case '4': + $copyResults[] = $currentVal; + break; + } + } + } + } + if (count($pubResults) > 0) { + $results = array_merge($results, $pubResults); + } else if (count($copyResults) > 0) { + $results = array_merge($results, $copyResults); + } + + return $results; + } + + /** + * Get the item's places of publication. + * + * @return array + */ + public function getPlacesOfPublication() + { + return $this->getPublicationInfo(); + } + + /** + * Get an array of playing times for the record (if applicable). + * + * @return array + */ + public function getPlayingTimes() + { + $times = $this->getFieldArray('306', array('a'), false); + + // Format the times to include colons ("HH:MM:SS" format). + for ($x = 0; $x < count($times); $x++) { + $times[$x] = substr($times[$x], 0, 2) . ':' . + substr($times[$x], 2, 2) . ':' . + substr($times[$x], 4, 2); + } + + return $times; + } + + /** + * Get an array of previous titles for the record. + * + * @return array + */ + public function getPreviousTitles() + { + // If the MARC links are being used, return blank array + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? array_map('trim', explode(',', $this->mainConfig->Record->marc_links)) + : array(); + return in_array('780', $fieldsNames) ? array() : parent::getPreviousTitles(); + } + + /** + * Get credits of people involved in production of the item. + * + * @return array + */ + public function getProductionCredits() + { + return $this->getFieldArray('508'); + } + + /** + * Get an array of publication frequency information. + * + * @return array + */ + public function getPublicationFrequency() + { + return $this->getFieldArray('310', array('a', 'b')); + } + + /** + * Get an array of strings describing relationships to other items. + * + * @return array + */ + public function getRelationshipNotes() + { + return $this->getFieldArray('580'); + } + + /** + * Get an array of all series names containing the record. Array entries may + * be either the name string, or an associative array with 'name' and 'number' + * keys. + * + * @return array + */ + public function getSeries() + { + $matches = array(); + + // First check the 440, 800 and 830 fields for series information: + $primaryFields = array( + '440' => array('a', 'p'), + '800' => array('a', 'b', 'c', 'd', 'f', 'p', 'q', 't'), + '830' => array('a', 'p')); + $matches = $this->getSeriesFromMARC($primaryFields); + if (!empty($matches)) { + return $matches; + } + + // Now check 490 and display it only if 440/800/830 were empty: + $secondaryFields = array('490' => array('a')); + $matches = $this->getSeriesFromMARC($secondaryFields); + if (!empty($matches)) { + return $matches; + } + + // Still no results found? Resort to the Solr-based method just in case! + return parent::getSeries(); + } + + /** + * Support method for getSeries() -- given a field specification, look for + * series information in the MARC record. + * + * @param array $fieldInfo Associative array of field => subfield information + * (used to find series name) + * + * @return array + */ + protected function getSeriesFromMARC($fieldInfo) + { + $matches = array(); + + // Loop through the field specification.... + foreach ($fieldInfo as $field => $subfields) { + // Did we find any matching fields? + $series = $this->marcRecord->getFields($field); + if (is_array($series)) { + foreach ($series as $currentField) { + // Can we find a name using the specified subfield list? + $name = $this->getSubfieldArray($currentField, $subfields); + if (isset($name[0])) { + $currentArray = array('name' => $name[0]); + + // Can we find a number in subfield v? (Note that number is + // always in subfield v regardless of whether we are dealing + // with 440, 490, 800 or 830 -- hence the hard-coded array + // rather than another parameter in $fieldInfo). + $number + = $this->getSubfieldArray($currentField, array('v')); + if (isset($number[0])) { + $currentArray['number'] = $number[0]; + } + + // Save the current match: + $matches[] = $currentArray; + } + } + } + } + + return $matches; + } + + /** + * Return an array of non-empty subfield values found in the provided MARC + * field. If $concat is true, the array will contain either zero or one + * entries (empty array if no subfields found, subfield values concatenated + * together in specified order if found). If concat is false, the array + * will contain a separate entry for each subfield value found. + * + * @param object $currentField Result from File_MARC::getFields. + * @param array $subfields The MARC subfield codes to read + * @param bool $concat Should we concatenate subfields? + * + * @return array + */ + protected function getSubfieldArray($currentField, $subfields, $concat = true) + { + // Start building a line of text for the current field + $matches = array(); + $currentLine = ''; + + // Loop through all subfields, collecting results that match the whitelist; + // note that it is important to retain the original MARC order here! + $allSubfields = $currentField->getSubfields(); + if (count($allSubfields) > 0) { + foreach ($allSubfields as $currentSubfield) { + if (in_array($currentSubfield->getCode(), $subfields)) { + // Grab the current subfield value and act on it if it is + // non-empty: + $data = trim($currentSubfield->getData()); + if (!empty($data)) { + // Are we concatenating fields or storing them separately? + if ($concat) { + $currentLine .= $data . ' '; + } else { + $matches[] = $data; + } + } + } + } + } + + // If we're in concat mode and found data, it will be in $currentLine and + // must be moved into the matches array. If we're not in concat mode, + // $currentLine will always be empty and this code will be ignored. + if (!empty($currentLine)) { + $matches[] = trim($currentLine); + } + + // Send back our result array: + return $matches; + } + + /** + * Get an array of summary strings for the record. + * + * @return array + */ + public function getSummary() + { + return $this->getFieldArray('520'); + } + + /** + * Get an array of technical details on the item represented by the record. + * + * @return array + */ + public function getSystemDetails() + { + return $this->getFieldArray('538'); + } + + /** + * Get an array of note about the record's target audience. + * + * @return array + */ + public function getTargetAudienceNotes() + { + return $this->getFieldArray('521'); + } + + /** + * Get the text of the part/section portion of the title. + * + * @return string + */ + public function getTitleSection() + { + return $this->getFirstFieldValue('245', array('n', 'p')); + } + + /** + * Get the statement of responsibility that goes with the title (i.e. "by John + * Smith"). + * + * @return string + */ + public function getTitleStatement() + { + return $this->getFirstFieldValue('245', array('c')); + } + + /** + * Get an array of lines from the table of contents. + * + * @return array + */ + public function getTOC() + { + // Return empty array if we have no table of contents: + $fields = $this->marcRecord->getFields('505'); + if (!$fields) { + return array(); + } + + // If we got this far, we have a table -- collect it as a string: + $toc = array(); + foreach ($fields as $field) { + $subfields = $field->getSubfields(); + foreach ($subfields as $subfield) { + // Break the string into appropriate chunks, and merge them into + // return array: + $toc = array_merge($toc, explode('--', $subfield->getData())); + } + } + return $toc; + } + + /** + * Get hierarchical place names (MARC field 752) + * + * returns an array of formatted hierarchical place names, consisting of all + * alpha-subfields, concatenated for display + * + * @return array + */ + public function getHierarchicalPlaceNames() + { + $placeNames = array(); + if ($fields = $this->marcRecord->getFields('752')) { + foreach ($fields as $field) { + $subfields = $field->getSubfields(); + $current = array(); + foreach ($subfields as $subfield) { + if (!is_numeric($subfield->getCode())) { + $current[] = $subfield->getData(); + } + } + $placeNames[] = implode(' -- ', $current); + } + } + return $placeNames; + } + + /** + * Return an array of associative URL arrays with one or more of the following + * keys: + * + * <li> + * <ul>desc: URL description text to display (optional)</ul> + * <ul>url: fully-formed URL (required if 'route' is absent)</ul> + * <ul>route: VuFind route to build URL with (required if 'url' is absent)</ul> + * <ul>routeParams: Parameters for route (optional)</ul> + * <ul>queryString: Query params to append after building route (optional)</ul> + * </li> + * + * @return array + */ + public function getURLs() + { + $retVal = array(); + + // Which fields/subfields should we check for URLs? + $fieldsToCheck = array( + '856' => array('y', 'z'), // Standard URL + '555' => array('a') // Cumulative index/finding aids + ); + + foreach ($fieldsToCheck as $field => $subfields) { + $urls = $this->marcRecord->getFields($field); + if ($urls) { + foreach ($urls as $url) { + // Is there an address in the current field? + $address = $url->getSubfield('u'); + if ($address) { + $address = $address->getData(); + + // Is there a description? If not, just use the URL itself. + foreach ($subfields as $current) { + $desc = $url->getSubfield($current); + if ($desc) { + break; + } + } + if ($desc) { + $desc = $desc->getData(); + } else { + $desc = $address; + } + + $retVal[] = array('url' => $address, 'desc' => $desc); + } + } + } + } + + return $retVal; + } + + /** + * Get all record links related to the current record. Each link is returned as + * array. + * Format: + * array( + * array( + * 'title' => label_for_title + * 'value' => link_name + * 'link' => link_URI + * ), + * ... + * ) + * + * @return null|array + */ + public function getAllRecordLinks() + { + // Load configurations: + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? explode(',', $this->mainConfig->Record->marc_links) : array(); + $useVisibilityIndicator + = isset($this->mainConfig->Record->marc_links_use_visibility_indicator) + ? $this->mainConfig->Record->marc_links_use_visibility_indicator : true; + + $retVal = array(); + foreach ($fieldsNames as $value) { + $value = trim($value); + $fields = $this->marcRecord->getFields($value); + if (!empty($fields)) { + foreach ($fields as $field) { + // Check to see if we should display at all + if ($useVisibilityIndicator) { + $visibilityIndicator = $field->getIndicator('1'); + if ($visibilityIndicator == '1') { + continue; + } + } + + // Get data for field + $tmp = $this->getFieldData($field); + if (is_array($tmp)) { + $retVal[] = $tmp; + } + } + } + } + return empty($retVal) ? null : $retVal; + } + + /** + * Support method for getFieldData() -- factor the relationship indicator + * into the field number where relevant to generate a note to associate + * with a record link. + * + * @param File_MARC_Data_Field $field Field to examine + * + * @return string + */ + protected function getRecordLinkNote($field) + { + // Normalize blank relationship indicator to 0: + $relationshipIndicator = $field->getIndicator('2'); + if ($relationshipIndicator == ' ') { + $relationshipIndicator = '0'; + } + + // Assign notes based on the relationship type + $value = $field->getTag(); + switch ($value) { + case '780': + if (in_array($relationshipIndicator, range('0', '7'))) { + $value .= '_' . $relationshipIndicator; + } + break; + case '785': + if (in_array($relationshipIndicator, range('0', '8'))) { + $value .= '_' . $relationshipIndicator; + } + break; + } + + return 'note_' . $value; + } + + /** + * Returns the array element for the 'getAllRecordLinks' method + * + * @param File_MARC_Data_Field $field Field to examine + * + * @return array|bool Array on success, boolean false if no + * valid link could be found in the data. + */ + protected function getFieldData($field) + { + // Make sure that there is a t field to be displayed: + if ($title = $field->getSubfield('t')) { + $title = $title->getData(); + } else { + return false; + } + + $linkTypeSetting = isset($this->mainConfig->Record->marc_links_link_types) + ? $this->mainConfig->Record->marc_links_link_types + : 'id,oclc,dlc,isbn,issn,title'; + $linkTypes = explode(',', $linkTypeSetting); + $linkFields = $field->getSubfields('w'); + + // Run through the link types specified in the config. + // For each type, check field for reference + // If reference found, exit loop and go straight to end + // If no reference found, check the next link type instead + foreach ($linkTypes as $linkType) { + switch (trim($linkType)){ + case 'oclc': + foreach ($linkFields as $current) { + if ($oclc = $this->getIdFromLinkingField($current, 'OCoLC')) { + $link = array('type' => 'oclc', 'value' => $oclc); + } + } + break; + case 'dlc': + foreach ($linkFields as $current) { + if ($dlc = $this->getIdFromLinkingField($current, 'DLC', true)) { + $link = array('type' => 'dlc', 'value' => $dlc); + } + } + break; + case 'id': + foreach ($linkFields as $current) { + if ($bibLink = $this->getIdFromLinkingField($current)) { + $link = array('type' => 'bib', 'value' => $bibLink); + } + } + break; + case 'isbn': + if ($isbn = $field->getSubfield('z')) { + $link = array( + 'type' => 'isn', 'value' => trim($isbn->getData()), + 'exclude' => $this->getUniqueId() + ); + } + break; + case 'issn': + if ($issn = $field->getSubfield('x')) { + $link = array( + 'type' => 'isn', 'value' => trim($issn->getData()), + 'exclude' => $this->getUniqueId() + ); + } + break; + case 'title': + $link = array('type' => 'title', 'value' => $title); + break; + } + // Exit loop if we have a link + if (isset($link)) { + break; + } + } + // Make sure we have something to display: + return !isset($link) ? false : array( + 'title' => $this->getRecordLinkNote($field), + 'value' => $title, + 'link' => $link + ); + } + + /** + * Returns an id extracted from the identifier subfield passed in + * + * @param \File_MARC_Subfield $idField MARC field containing id information + * @param string $prefix Prefix to search for in id field + * @param bool $raw Return raw match, or normalize? + * + * @return string|bool ID on success, false on failure + */ + protected function getIdFromLinkingField($idField, $prefix = null, $raw = false) + { + $text = $idField->getData(); + if (preg_match('/\(([^)]+)\)(.+)/', $text, $matches)) { + // If prefix matches, return ID: + if ($matches[1] == $prefix) { + // Special case -- LCCN should not be stripped: + return $raw + ? $matches[2] + : trim(str_replace(range('a', 'z'), '', ($matches[2]))); + } + } else if ($prefix == null) { + // If no prefix was given or found, we presume it is a raw bib record + return $text; + } + return false; + } + + /** + * Get Status/Holdings Information from the internally stored MARC Record + * (support method used by the NoILS driver). + * + * @param array $field The MARC Field to retrieve + * @param array $data A keyed array of data to retrieve from subfields + * + * @return array + */ + public function getFormattedMarcDetails($field, $data) + { + // Initialize return array + $matches = array(); + $i = 0; + + // Try to look up the specified field, return empty array if it doesn't + // exist. + $fields = $this->marcRecord->getFields($field); + if (!is_array($fields)) { + return $matches; + } + + // Extract all the requested subfields, if applicable. + foreach ($fields as $currentField) { + foreach ($data as $key => $info) { + $split = explode("|", $info); + if ($split[0] == "msg") { + if ($split[1] == "true") { + $result = true; + } elseif ($split[1] == "false") { + $result = false; + } else { + $result =$split[1]; + } + $matches[$i][$key] = $result; + } else { + // Default to subfield a if nothing is specified. + if (count($split) < 2) { + $subfields = array('a'); + } else { + $subfields = str_split($split[1]); + } + $result = $this->getSubfieldArray( + $currentField, $subfields, true + ); + $matches[$i][$key] = count($result) > 0 + ? (string)$result[0] : ''; + } + } + $matches[$i]['id'] = $this->getUniqueID(); + $i++; + } + return $matches; + } + + /** + * Return an XML representation of the record using the specified format. + * Return false if the format is unsupported. + * + * @param string $format Name of format to use (corresponds with OAI-PMH + * metadataPrefix parameter). + * @param string $baseUrl Base URL of host containing VuFind (optional; + * may be used to inject record URLs into XML when appropriate). + * @param RecordLink $recordLink Record link helper (optional; may be used to + * inject record URLs into XML when appropriate). + * + * @return mixed XML, or false if format unsupported. + */ + public function getXML($format, $baseUrl = null, $recordLink = null) + { + // Special case for MARC: + if ($format == 'marc21') { + $xml = $this->marcRecord->toXML(); + $xml = str_replace( + array(chr(27), chr(28), chr(29), chr(30), chr(31)), ' ', $xml + ); + $xml = simplexml_load_string($xml); + if (!$xml || !isset($xml->record)) { + return false; + } + + // Set up proper namespacing and extract just the <record> tag: + $xml->record->addAttribute('xmlns', "http://www.loc.gov/MARC21/slim"); + $xml->record->addAttribute( + 'xsi:schemaLocation', + 'http://www.loc.gov/MARC21/slim ' . + 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd', + 'http://www.w3.org/2001/XMLSchema-instance' + ); + $xml->record->addAttribute('type', 'Bibliographic'); + return $xml->record->asXML(); + } + + // Try the parent method: + return parent::getXML($format, $baseUrl, $recordLink); + } + + /** + * Attach an ILS connection and related logic to the driver + * + * @param \VuFind\ILS\Connection $ils ILS connection + * @param \VuFind\ILS\Logic\Holds $holdLogic Hold logic handler + * @param \VuFind\ILS\Logic\TitleHolds $titleHoldLogic Title hold logic handler + * + * @return void + */ + public function attachILS(\VuFind\ILS\Connection $ils, + \VuFind\ILS\Logic\Holds $holdLogic, + \VuFind\ILS\Logic\TitleHolds $titleHoldLogic + ) { + $this->ils = $ils; + $this->holdLogic = $holdLogic; + $this->titleHoldLogic = $titleHoldLogic; + } + + /** + * Do we have an attached ILS connection? + * + * @return bool + */ + protected function hasILS() + { + return null !== $this->ils; + } + + /** + * Get an array of information about record holdings, obtained in real-time + * from the ILS. + * + * @return array + */ + public function getRealTimeHoldings() + { + return $this->hasILS() + ? $this->holdLogic->getHoldings($this->getUniqueID()) + : array(); + } + + /** + * Get an array of information about record history, obtained in real-time + * from the ILS. + * + * @return array + */ + public function getRealTimeHistory() + { + // Get Acquisitions Data + if (!$this->hasILS()) { + return array(); + } + try { + return $this->ils->getPurchaseHistory($this->getUniqueID()); + } catch (ILSException $e) { + return array(); + } + } + + /** + * Get a link for placing a title level hold. + * + * @return mixed A url if a hold is possible, boolean false if not + */ + public function getRealTimeTitleHold() + { + if ($this->hasILS()) { + $biblioLevel = strtolower($this->getBibliographicLevel()); + if ("monograph" == $biblioLevel || strstr("part", $biblioLevel)) { + if ($this->ils->getTitleHoldsMode() != "disabled") { + return $this->titleHoldLogic->getHold($this->getUniqueID()); + } + } + } + + return false; + } + + /** + * Returns true if the record supports real-time AJAX status lookups. + * + * @return bool + */ + public function supportsAjaxStatus() + { + return true; + } + + /** + * Get access to the raw File_MARC object. + * + * @return File_MARCBASE + */ + public function getMarcRecord() + { + return $this->marcRecord; + } + + /** + * Get an XML RDF representation of the data in this record. + * + * @return mixed XML RDF data (empty if unsupported or error). + */ + public function getRDFXML() + { + return XSLTProcessor::process( + 'record-rdf-mods.xsl', trim($this->marcRecord->toXML()) + ); + } +} diff --git a/module/finc/src/finc/RecordDriver/SolrMarcRemote.php b/module/finc/src/finc/RecordDriver/SolrMarcRemote.php index 7c307fe7ca3..2604a0ad79d 100644 --- a/module/finc/src/finc/RecordDriver/SolrMarcRemote.php +++ b/module/finc/src/finc/RecordDriver/SolrMarcRemote.php @@ -22,7 +22,9 @@ * * @category VuFind2 * @package RecordDrivers - * @author André Lahmann <lahmann@ub.uni-leipzig.de>, Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:record_drivers Wiki */ @@ -35,11 +37,13 @@ use \Zend\Log\LoggerInterface; * * @category VuFind2 * @package RecordDrivers - * @author André Lahmann <lahmann@ub.uni-leipzig.de>, Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:record_drivers Wiki */ -class SolrMarcRemote extends \VuFind\RecordDriver\SolrMarc +class SolrMarcRemote extends SolrMarc { /** * Logger (or false for none) @@ -69,6 +73,13 @@ class SolrMarcRemote extends \VuFind\RecordDriver\SolrMarc */ protected $mainConfig; + /** + * holds searches.ini data + * + * @var array + */ + protected $searchesConfig; + /** * Constructor * @@ -95,6 +106,7 @@ class SolrMarcRemote extends \VuFind\RecordDriver\SolrMarc } $this->mainConfig = $mainConfig; + $this->searchesConfig = $searchSettings; } /** @@ -173,7 +185,7 @@ class SolrMarcRemote extends \VuFind\RecordDriver\SolrMarc * @throws \Exception * @throws \File_MARC_Exception */ - private function getRemoteData() { + protected function getRemoteData() { // handle availability of fullrecord if (isset($this->fields['fullrecord'])) { diff --git a/module/finc/src/finc/RecordDriver/SolrMarcRemoteFinc.php b/module/finc/src/finc/RecordDriver/SolrMarcRemoteFinc.php new file mode 100644 index 00000000000..af2cfc78b95 --- /dev/null +++ b/module/finc/src/finc/RecordDriver/SolrMarcRemoteFinc.php @@ -0,0 +1,1020 @@ +<?php +/** + * finc specific model for MARC records without a fullrecord in Solr. The fullrecord is being + * retrieved from an external source. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +namespace finc\RecordDriver; + +/** + * finc specific model for MARC records without a fullrecord in Solr. The fullrecord is being + * retrieved from an external source. + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +class SolrMarcRemoteFinc extends SolrMarcRemote +{ + + /** + * pattern to identify bsz + */ + const BSZ_PATTERN = '/^(\(DE-576\))(\d+)(\w|)/'; + + /** + * @var string ISIL of this instance's library + */ + protected $isil = ''; + + /** + * @var array Array of ISILs set in the LibraryGroup section in config.ini. + */ + protected $libraryGroup = array(); + + /** + * @var string|null + * @link https://intern.finc.info/fincproject/projects/finc-intern/wiki/FincMARC_-_Erweiterung_von_MARC21_f%C3%BCr_finc + */ + protected $localMarcFieldOfLibrary = null; + + /** + * Constructor + * + * @param \Zend\Config\Config $mainConfig VuFind main configuration (omit for + * built-in defaults) + * @param \Zend\Config\Config $recordConfig Record-specific configuration file + * (omit to use $mainConfig as $recordConfig) + * @param \Zend\Config\Config $searchSettings Search-specific configuration file + */ + public function __construct($mainConfig = null, $recordConfig = null, + $searchSettings = null + ) + { + parent::__construct($mainConfig, $recordConfig, $searchSettings); + + if (isset($mainConfig->InstitutionInfo->isil)) { + $this->isil = $this->mainConfig->InstitutionInfo->isil; + } else { + $this->debug('InstitutionInfo setting is missing.'); + } + + if (isset($mainConfig->LibraryGroup->libraries)) { + $this->libraryGroup = explode(',' , $this->mainConfig->LibraryGroup->libraries); + } else { + $this->debug('LibraryGroup setting is missing.'); + } + + if (isset($this->mainConfig->CustomSite->namespace)) { + // map for marc fields + $map = [ + 'che' => '971', + 'hgb' => '979', + 'hfbk' => '978', + 'hfm' => '977', + 'hmt' => '970', + 'htw' => '973', + 'htwk' => '974', + 'tuf' => '972', + 'ubl' => '969', + 'zit' => '976', + 'zwi' => '975', + ]; + $this->localMarcFieldOfLibrary = + isset($map[$this->mainConfig->CustomSite->namespace]) ? + $map[$this->mainConfig->CustomSite->namespace] : null; + } else { + $this->debug('Namespace setting for localMarcField is missing.'); + } + } + + /** + * Return an array of associative URL arrays with one or more of the following + * keys: + * + * <li> + * <ul>desc: URL description text to display (optional)</ul> + * <ul>url: fully-formed URL (required if 'route' is absent)</ul> + * <ul>route: VuFind route to build URL with (required if 'url' is absent)</ul> + * <ul>routeParams: Parameters for route (optional)</ul> + * <ul>queryString: Query params to append after building route (optional)</ul> + * </li> + * + * @return array + */ + public function getURLs() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $retVal = array(); + + // Which fields/subfields should we check for URLs? + $fieldsToCheck = array( + '856' => array('u'), // Standard URL + '555' => array('a') // Cumulative index/finding aids + ); + + foreach ($fieldsToCheck as $field => $subfields) { + $urls = $this->marcRecord->getFields($field); + if ($urls) { + foreach ($urls as $url) { + + $isil = $url->getSubfield('9'); + + $isISIL = false; + + if($isil) { + $isil = $isil->getData(); + if(preg_match('/'.$this->isil.'.*/', $isil)) { + $isISIL = true; + } + } else { + $isISIL = true; + } + + if($isISIL) { + + // Is there an address in the current field? + $address = $url->getSubfield('u'); + if ($address) { + $address = $address->getData(); + + // Is there a description? If not, just use the URL itself. + foreach (array('3', 'y', 'z', 'x') as $current) { + $desc = $url->getSubfield($current); + if ($desc) { + break; + } + } + if ($desc) { + $desc = $desc->getData(); + } else { + $desc = $address; + } + + // If url doesn't exist as key so far write to return variable. + if (!in_array(array('url' => $address, 'desc' => $desc), $retVal)) { + $retVal[] = array('url' => $address, 'desc' => $desc); + } + } + } + } + } + } + return $retVal; + } + + /** + * Return the local callnumber. + * + * @todo Optimization by removing of prefixed isils + * + * @return array Return fields. + * @access public + * @link https://intern.finc.info/issues/2639 + */ + public function getLocalCallnumber() + { + $array = array(); + + if (count($this->libraryGroup) > 0 && isset($this->fields['itemdata'])) + { + $itemdata = json_decode($this->fields['itemdata'], true); + if (count($itemdata) > 0) { + // error_log('Test: '. print_r($this->fields['itemdata'], true)); + $i = 0; + foreach ($this->libraryGroup as $isil) { + if (isset($itemdata[$isil])) { + foreach ($itemdata[$isil] as $val) { + $array[$i]['barcode'] = '(' . $isil . ')' . $val['bc']; + $array[$i]['callnumber'] = '(' . $isil . ')' . $val['cn']; + $i++; + } + } // end if + } // end foreach + } // end if + } // end if + return $array; + } + + /** + * Get local callnumbers of a special library. + * + * @return array + * @access protected + */ + protected function getLocalCallnumbersByLibrary() + { + $array = array(); + $callnumbers = array(); + + if (count($this->libraryGroup) > 0 && isset($this->fields['itemdata'])) + { + $itemdata = json_decode($this->fields['itemdata'], true); + if (count($itemdata) > 0) { + $i = 0; + foreach ($this->libraryGroup as $isil) { + if (isset($itemdata[$isil])) { + foreach ($itemdata[$isil] as $val) { + // exclude equal callnumbers + if (false == in_array($val['cn'], $callnumbers)) { + $array[$i]['callnumber'] = $val['cn']; + $array[$i]['location'] = $isil; + $callnumbers[] = $val['cn']; + $i++; + } + } // end foreach + } // end if + } // end foreach + } // end if + } // end if + unset($callnumbers); + return $array; + } + + /** + * Get the special local call number; for the moment only used by the + * university library of Freiberg at finc marc 972i. + * + * @return string + * @access protected + */ + protected function getLocalGivenCallnumber() + { + $retval = array(); + $arrSignatur = $this->getFieldArray($this->localMarcFieldOfLibrary, array('i')); + + foreach ($arrSignatur as $signatur) { + foreach ($this->libraryGroup as $code) { + if (0 < preg_match('/^\('.$code.'\)/', $signatur)) { + + $retval[] = preg_replace( '/^\('.$code.'\)/','', $signatur); + } + } + } + return $retval; + } + + /** + * Get an array of supplements and special issue entry. + * + * @link http://www.loc.gov/marc/bibliographic/bd770.html + * @return array + * @access protected + */ + protected function getSupplements() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + //return $this->_getFieldArray('770', array('i','t')); // has been originally 'd','h','n','x' but only 'i' and 't' for ubl requested; + $array = array(); + $supplement = $this->marcRecord->getFields('770'); + // if not return void value + if (!$supplement) { + return $array; + } // end if + + foreach ($supplement as $key => $line) { + $array[$key]['pretext'] = ($line->getSubfield('i')) + ? $line->getSubfield('i')->getData() : ''; + $array[$key]['text'] = ($line->getSubfield('t')) + ? $line->getSubfield('t')->getData() : ''; + // get ppns of bsz + $linkFields = $line->getSubfields('w'); + foreach ($linkFields as $current) { + $text = $current->getData(); + // Extract parenthetical prefixes: + if (preg_match(self::BSZ_PATTERN, $text, $matches)) { + //$id = $this->checkIfRecordExists($matches[2]); + //if ($id != null) { + $array[$key]['record_id'] = $matches[2].$matches[3]; + //} + //break; + } + } // end foreach + } // end foreach + + return $this->addFincIDToRecord($array); + } + + /** + * Special method to extracting the index of German prints of the marc21 + * field 024 indicator 8 subfield a + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/1442 + */ + protected function getIndexOfGermanPrints() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + // define a false indicator + $lookfor_indicator = '8'; + $retval = array(); + + $fields = $this->marcRecord->getFields('024'); + if (!$fields) { + return null; + } + foreach ($fields as $field) { + // ->getIndicator(position) + $subjectrow = $field->getIndicator('1'); + if ($subjectrow == $lookfor_indicator) { + if ($subfield = $field->getSubfield('a')){ + if (preg_match('/^VD/i', $subfield->getData()) > 0) { + $retval[] = $subfield->getData(); + } + } + } + } + // echo "<pre>"; print_r($retval); echo "</pre>"; + return $retval; + } + + /** + * Get an array of instrumentation notes taken from the local data + * of the Petrucci music library subfield 590b + * + * @return array + * @access protected + */ + protected function getInstrumentation() + { + return $this->getFieldArray('590', array('b')); + } + + /** + * Get the ISSN from a record. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/969 description + */ + protected function getISSN() + { + return $this->getFieldArray('022', array('a')); + } + + /** + * Get the ISSN from a the parallel title of a record. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/969 description + */ + protected function getISSNsParallelTitles() + { + return $this->getFieldArray('029', array('a')); + } + + /** + * Get an array of information about Journal holdings realised for the + * special needs of University library of Chemnitz. MAB fields 720. + * + * @return array + * @access public + * @link https://intern.finc.info/fincproject/issues/338 + */ + public function getJournalHoldings() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $retval = array(); + $match = array(); + + // Get ID and connect to catalog + //$catalog = ConnectionManager::connectToCatalog(); + //$terms = $catalog->getConfig('OrderJournalTerms'); + + $fields = $this->marcRecord->getFields('971'); + if (!$fields) { + return array(); + } + + $key = 0; + foreach ($fields as $field) { + /*if ($subfield = $field->getSubfield('j')) { + preg_match('/\(.*\)(.*)/', $subfield->getData(), $match); + $retval[$key]['callnumber'] = trim($match[1]); + }*/ + if ($subfield = $field->getSubfield('k')) { + preg_match('/(.*)##(.*)##(.*)/', trim($subfield->getData()), $match); + $retval[$key]['callnumber'] = trim($match[1]); + $retval[$key]['holdings'] = trim($match[2]); + $retval[$key]['footnote'] = trim($match[3]); + // deprecated check if a certain wording exist + // $retval[$key]['is_holdable'] = (in_array(trim($match[3]), $terms['terms'])) ? 1 : 0; + // if subfield k exists so make journal holdable + $retval[$key]['is_holdable'] = 1; + + if (count($this->getBarcode()) == 1) { + $current = $this->getBarcode(); + $barcode = $current[0]; + } else { + $barcode = ''; + } + // deprecated check if a certain wording exist + // $retval[$key]['link'] = (in_array(trim($match[3]), $terms['terms'])) ? '/Record/' . $this->getUniqueID() .'/HoldJournalCHE?callnumber=' . urlencode($retval[$key]['callnumber']) .'&barcode=' . $barcode : ''; + // if subfield k exists so make journal holdable + $retval[$key]['link'] = '/Record/' . $this->getUniqueID() .'/HoldJournalCHE?callnumber=' . urlencode($retval[$key]['callnumber']) .'&barcode=' . $barcode; + //var_dump($retval[$key]['is_holdable'], $terms); + $key++; + } + + } + return $retval; + } + + /** + * Return a local access number for call number. + * Marc field depends on library e.g. 975 for WHZ. + * Seems to be very extraordinary special case. + * + * @return array + * @access protected + * @link https://intern.finc.info/issues/1302 + */ + protected function getLocalAccessNumber() + { + if (null != $this->localMarcFieldOfLibrary) { + return $this->getFieldArray($this->localMarcFieldOfLibrary, array('o')); + } + return array(); + } + + /** + * Get all local class subjects. First realization for HGB. + * + * @return array + * @access protected + * @link https://intern.finc.info/issues/2626 + */ + protected function getLocalClassSubjects() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $array = array(); + $classsubjects = $this->marcRecord->getFields('979'); + // if not return void value + if (!$classsubjects) { + return $array; + } // end if + foreach ($classsubjects as $key => $line) { + // if subfield with class subjects exists + if ($line->getSubfield('f')) { + // get class subjects + $array[$key]['nb'] = $line->getSubfield('f')->getData(); + } // end if subfield a + if ($line->getSubfield('9')) { + $array[$key]['data'] = $line->getSubfield('9')->getData(); + /* $tmp = $line->getSubfield('9')->getData(); + $tmpArray = array(); + $data = explode(',', $tmp); + if(is_array($data) && (count($data) > 0)) { + foreach ($data as $value) { + $tmpArray[] = $value; + } + } + if(count($tmpArray) > 0) { + $array[$key]['data'] = $tmpArray; + } else { + $array[$key]['data'] = $data; + }*/ + } + } // end foreach + return $array; + } + + + /** + * Returning local format field of a library using an consortial defined + * field with subfield $c. Marc field depends on library e.g. 970 for HMT or + * 972 for TUBAF + * + * @return array + * @access protected + */ + public function getLocalFormat() + { + if (null != $this->localMarcFieldOfLibrary) { + if (count($localformat = $this->getFieldArray($this->localMarcFieldOfLibrary, array('c'))) > 0) { + foreach ($localformat as &$line) { + if ($line != "") { + $line = trim('local_format_' . strtolower($line)); + } + } + unset($line); + return $localformat; + } + } + return array(); + } + + /** + * Return a local notice via an consortial defined field with subfield $k. + * Marc field depends on library e.g. 970 for HMT or 972 for TUBAF. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/1308 + */ + protected function getLocalNotice() + { + if (null != $this->localMarcFieldOfLibrary) { + return $this->getFieldArray($this->localMarcFieldOfLibrary, array('k')); + } + return array(); + } + + /** + * Get an array of musical heading based on a swb field + * at the marc field. + * + * @return mixed null if there's no field or array with results + * @access protected + */ + protected function getMusicHeading() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $retval = array(); + + $fields = $this->marcRecord->getFields('937'); + if (!$fields) { + return null; + } + foreach ($fields as $key => $field) { + if ($d = $field->getSubfield('d')) { + $retval[$key][] = $d->getData(); + } + if ($e = $field->getSubfield('e')) { + $retval[$key][] = $e->getData(); + } + if ($f = $field->getSubfield('f')) { + $retval[$key][] = $f->getData(); + } + } + return $retval; + } + + /** + * Get notice of a title representing a special case of University + * library of Chemnitz: MAB field 999l + * + * @return string + * @access protected + */ + protected function getNotice() + { + return $this->getFirstFieldValue('971', array('l')); + } + + /** + * Get an array of style/genre of a piece taken from the local data + * of the Petrucci music library subfield 590a + * + * @return array + * @access protected + */ + protected function getPieceStyle() + { + return $this->getFieldArray('590', array('a')); + } + + /** + * Get specific marc information about parallel editions. Unflexible solution + * for HMT only implemented. + * + * @todo more flexible implementation + * + * @return array + * @access protected + * @link https://intern.finc.info/issues/4327 + */ + protected function getParallelEditions() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $array = array(); + $fields = array('775'); + $i = 0; + + foreach ($fields as $field) { + + $related = $this->marcRecord->getFields($field); + // if no entry break it + if ($related) { + foreach ($related as $key => $line) { + // check if subfields i or t exist. if yes do a record. + if ($line->getSubfield('i') || $line->getSubfield('t')) { + $array[$i]['identifier'] = ($line->getSubfield('i')) + ? $line->getSubfield('i')->getData() : ''; + $array[$i]['text'] = ($line->getSubfield('t')) + ? $line->getSubfield('t')->getData() : ''; + // get ppns of bsz + $linkFields = $line->getSubfields('w'); + if (is_array($linkFields) && count($linkFields) > 0) { + foreach ($linkFields as $current) { + $text = $current->getData(); + // Extract parenthetical prefixes: + if (preg_match(self::BSZ_PATTERN, $text, $matches)) { + $array[$key]['record_id'] = $matches[2].$matches[3]; + } + } // end foreach + } // end if + $i++; + } // end if + } // end foreach + } + } + return $this->addFincIDToRecord($array); + } + + /** + * Get an array of previous titles for the record. + * + * @todo use HttpService for URL query + * + * @return string + * @access protected + */ + public function getPrice() + { + $currency = $this->getFirstFieldValue('365', array('c')); + $price = $this->getFirstFieldValue('365', array('b')); + if (!empty($currency) && !empty($price) ) { + // if possible convert it in euro + if (is_array($converted = + json_decode(str_replace( + array('lhs','rhs','error','icc'), + array('"lhs"','"rhs"','"error"','"icc"'), + file_get_contents("http://www.google.com/ig/calculator?q=".$price.$currency."=?EUR") + ),true) + )) { + if(empty($converted['error'])){ + $rhs = explode(' ', trim($converted['rhs'])); + return money_format('%.2n', $rhs[0]); + } + } + return $currency . " ". $price; + } + return ""; + } + + /** + * Get the provenience of a title. + * + * @return array + * @access protected + */ + protected function getProvenience() + { + return $this->getFieldArray('561', array('a')); + } + + /** + * Checked if an title is ordered by the library using an consortial defined + * field with subfield $m. Marc field depends on library e.g. 970 for HMT or + * 972 for TUBAF + * + * @return bool + * @access protected + */ + protected function getPurchaseInformation() + { + if (null != $this->localMarcFieldOfLibrary) { + if ( $this->getFirstFieldValue($this->localMarcFieldOfLibrary, array('m')) == 'e') { + return true; + } + } + return false; + } + + /** + * Get a short list of series for ISBD citation style + * + * @return array + * @access protected + * @link http://www.loc.gov/marc/bibliographic/bd830.html + * @link https://intern.finc.info/fincproject/issues/457 + */ + protected function getSeriesWithVolume() + { + return $this->getFieldArray('830', array('a','v'), false); + } + + /** + * Get local classification of UDK. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/1135 + */ + protected function getUDKs() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $array = array(); + if (null != $this->localMarcFieldOfLibrary) { + + $udk = $this->marcRecord->getFields($this->localMarcFieldOfLibrary); + // if not return void value + if (!$udk) { + return $array; + } // end if + + foreach ($udk as $key => $line) { + // if subfield with udk exists + if ($line->getSubfield('f')) { + // get udk + $array[$key]['index'] = $line->getSubfield('f')->getData(); + // get udk notation + // fixes by update of File_MARC to version 0.8.0 + // @link https://intern.finc.info/issues/2068 + /* + if ($notation = $line->getSubfield('n')) { + // get first value + $array[$key]['notation'][] = $notation->getData(); + // iteration over udk notation + while ($record = $notation->next()) { + $array[$key]['notation'][] = $record->getData(); + $notation = $record; + } + } // end if subfield n + unset($notation); + */ + if ($record = $line->getSubfields('n')) { + // iteration over rvk notation + foreach ($record as $field) { + $array[$key]['notation'][] = $field->getData(); + } + } // end if subfield n + } // end if subfield f + } // end foreach + } + //error_log(print_r($array, true)); + return $array; + } + + /** + * Get addional entries for personal names. + * + * @return array + * @access protected + * @link http://www.loc.gov/marc/bibliographic/bd700.html + */ + protected function getAdditionalAuthors() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + // result array to return + $retval = array(); + + $results = $this->marcRecord->getFields('700'); + if (!$results) { + return $retval; + } + + foreach ($results as $key => $line) { + $retval[$key]['name'] = ($line->getSubfield('a')) + ? $line->getSubfield('a')->getData() : ''; + $retval[$key]['dates'] = ($line->getSubfield('d')) + ? $line->getSubfield('d')->getData() : ''; + $retval[$key]['relator'] = ($line->getSubfield('e')) + ? $line->getSubfield('e')->getData() : ''; + } + // echo "<pre>"; print_r($retval); echo "</pre>"; + return $retval; + } + + /** + * Get specific marc information about additional items. Unflexible solution + * for UBL only implemented. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/1315 + */ + protected function getAdditionals() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $array = array(); + $fields = array('770','775','776'); + $i = 0; + + foreach ($fields as $field) { + + $related = $this->marcRecord->getFields($field); + // if no entry break it + if ($related) { + foreach ($related as $key => $line) { + // check if subfields i or t exist. if yes do a record. + if ($line->getSubfield('i') || $line->getSubfield('t')) { + $array[$i]['identifier'] = ($line->getSubfield('i')) + ? $line->getSubfield('i')->getData() : ''; + $array[$i]['text'] = ($line->getSubfield('t')) + ? $line->getSubfield('t')->getData() : ''; + // get ppns of bsz + $linkFields = $line->getSubfields('w'); + if (is_array($linkFields) && count($linkFields) > 0) { + foreach ($linkFields as $current) { + $text = $current->getData(); + // Extract parenthetical prefixes: + if (preg_match(self::BSZ_PATTERN, $text, $matches)) { + $array[$i]['record_id'] = $matches[2].$matches[3]; + } + } // end foreach + } // end if + $i++; + } // end if + } // end foreach + } + } + return $this->addFincIDToRecord($array); + } + + /** + * Special method to extracting the data of the marc21 field 689 of the + * the bsz heading subjects chains. + * + * @return array + * @access protected + */ + protected function getAllSubjectHeadingsExtended() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + // define a false indicator + $firstindicator = 'x'; + $retval = array(); + + $fields = $this->marcRecord->getFields('689'); + if (!$fields) { + return null; + } + foreach ($fields as $field) { + $subjectrow = $field->getIndicator('1'); + if ($subjectrow != $firstindicator) { + $key = (isset($key) ? $key +1 : 0); + $firstindicator = $subjectrow; + } + if ($subfield = $field->getSubfield('a')){ + $retval[$key]['subject'][] = $subfield->getData(); + } + if ($subfield = $field->getSubfield('t')){ + $retval[$key]['subject'][] = $subfield->getData(); + } + if ($subfield = $field->getSubfield('9')){ + $retval[$key]['subsubject'] = $subfield->getData(); + } + } + return $retval; + } + + /** + * Return all barcode of finc marc 983 $a at full marc record. + * + * @param string Prefixes of library seals. + * + * @return array List of barcodes. + * @access protected + */ + protected function getBarcode() + { + + $barcodes = array(); + + //$driver = ConnectionManager::connectToCatalog(); + //$libraryCodes = $driver->getIniFieldAsArray('searches','LibraryGroup'); + $libraryCodes = $this->searchesConfig->LibrarayGroup; + + // get barcodes from marc + $barcodes = $this->getFieldArray('983', array('a')); + + if (!isset($libraryCodes->libraries)) { + return $barcodes; + } else { + if (count($barcodes) > 0) { + $codes = explode(",", $libraryCodes->libraries); + $match = array(); + $retval = array(); + foreach($barcodes as $barcode) { + if (preg_match('/^\((.*)\)(.*)$/', trim($barcode), $match)); + if ( in_array($match[1], $codes) ) { + $retval[] = $match[2]; + } + } // end foreach + if (count($retval) > 0 ) { + return $retval; + } + } + } + return array(); + } + + /** + * Get the catalogue or opus number of a title. Implemented + * for petrucci music library. + * + * @return array + * @access protected + */ + protected function getCatalogueNumber() + { + return $this->getFieldArray('245', array('b')); + } + + /** + * Get an array of content notes. + * + * @return array + * @access protected + */ + protected function getContentNote() + { + return $this->getFieldArray('505', array('t')); + } + + /** + * Get dissertation notes for the record. + * + * @return array + * @access protected + */ + protected function getDissertationNote() + { + return $this->getFieldArray('502', array('a')); + } + + /** + * Get id of related items + * + * @return string + * @access protected + */ + protected function getRelatedItems() + { + return $this->getFirstFieldValue('776', array('z')); + } +} -- GitLab