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