diff --git a/config/vufind/OpenUrlRules.json b/config/vufind/OpenUrlRules.json
new file mode 100644
index 0000000000000000000000000000000000000000..a93c9792b1be742f8e6ec25454aa33f481a3da24
--- /dev/null
+++ b/config/vufind/OpenUrlRules.json
@@ -0,0 +1,17 @@
+[
+  {
+    "__comment" : "documentation: https://vufind.org/wiki/vufind2:openurls#defining_openurl_rules",
+    "exclude" : [],
+    "include" :
+    [
+      {
+        "methods": {
+          "getCleanISSN":"*"
+        }
+      },
+      {
+        "recorddriver":"VuFind\\RecordDriver\\Pazpar2"
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php b/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php
index fa72063a578355efbc35b878ec37cc9d51232693..8d37e3f99ce90470295948fee16fb58184d434f0 100644
--- a/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php
+++ b/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php
@@ -384,50 +384,33 @@ abstract class AbstractBase implements \VuFind\Db\Table\DbTableAwareInterface,
     }
 
     /**
-     * Does the OpenURL configuration indicate that we should display OpenURLs in
-     * the specified context?
-     *
-     * @param string $area 'results', 'record' or 'holdings'
+     * Returns true if the record supports real-time AJAX status lookups.
      *
      * @return bool
      */
-    public function openURLActive($area)
+    public function supportsAjaxStatus()
     {
-        // Doesn't matter the target area if no OpenURL resolver is specified:
-        if (!isset($this->mainConfig->OpenURL->url)) {
-            return false;
-        }
-
-        // If a setting exists, return that:
-        $key = 'show_in_' . $area;
-        if (isset($this->mainConfig->OpenURL->$key)) {
-            return $this->mainConfig->OpenURL->$key;
-        }
-
-        // If we got this far, use the defaults -- true for results, false for
-        // everywhere else.
-        return ($area == 'results');
+        return false;
     }
 
     /**
-     * Should we display regular URLs when an OpenURL is present?
+     * Checks the current record if it's supported for generating OpenURLs.
      *
      * @return bool
      */
-    public function replaceURLsWithOpenURL()
+    public function supportsOpenUrl()
     {
-        return isset($this->mainConfig->OpenURL->replace_other_urls)
-            ? $this->mainConfig->OpenURL->replace_other_urls : false;
+        return true;
     }
 
     /**
-     * Returns true if the record supports real-time AJAX status lookups.
+     * Checks the current record if it's supported for generating COinS-OpenURLs.
      *
      * @return bool
      */
-    public function supportsAjaxStatus()
+    public function supportsCoinsOpenUrl()
     {
-        return false;
+        return true;
     }
 
     /**
diff --git a/module/VuFind/src/VuFind/RecordDriver/EIT.php b/module/VuFind/src/VuFind/RecordDriver/EIT.php
index 7c2ee6bd32196a97564517054e97aeb64e2c877c..3a13b6c0a8681b3b6088c476210178028da9f59f 100644
--- a/module/VuFind/src/VuFind/RecordDriver/EIT.php
+++ b/module/VuFind/src/VuFind/RecordDriver/EIT.php
@@ -442,25 +442,12 @@ class EIT extends SolrDefault
             ? $this->controlInfo['artinfo']['tig']['atl'] : '';
     }
 
-        /**
-     * Does the OpenURL configuration indicate that we should display OpenURLs in
-     * the specified context?
-     *
-     * @param string $area 'results', 'record' or 'holdings'
-     *
-     * @return bool
-     */
-    public function openURLActive($area)
-    {
-        return true;
-    }
-
     /**
-     * Support method for getOpenURL() -- pick the OpenURL format.
+     * Support method for getOpenUrl() -- pick the OpenURL format.
      *
      * @return string
      */
-    protected function getOpenURLFormat()
+    protected function getOpenUrlFormat()
     {
         // If we have multiple formats, Book, Journal and Article are most
         // important...
diff --git a/module/VuFind/src/VuFind/RecordDriver/Pazpar2.php b/module/VuFind/src/VuFind/RecordDriver/Pazpar2.php
index aeb91312d89d9cff4e70f1130cf352f646fdf8d9..def57943aa1d11190206553e2139c83e172a6b2f 100644
--- a/module/VuFind/src/VuFind/RecordDriver/Pazpar2.php
+++ b/module/VuFind/src/VuFind/RecordDriver/Pazpar2.php
@@ -242,24 +242,11 @@ class Pazpar2 extends SolrDefault
     }
 
     /**
-     * Does the OpenURL configuration indicate that we should display OpenURLs in
-     * the specified context?
-     *
-     * @param string $area 'results', 'record' or 'holdings'
-     *
-     * @return bool
-     */
-    public function openURLActive($area)
-    {
-        return AbstractBase::openURLActive($area);
-    }
-
-    /**
-     * Support method for getOpenURL() -- pick the OpenURL format.
+     * Support method for getOpenUrl() -- pick the OpenURL format.
      *
      * @return string
      */
-    protected function getOpenURLFormat()
+    protected function getOpenUrlFormat()
     {
         return 'Book';
     }
diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php b/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php
index 1ca12ae421bc02226698d365323dad9520decd54..4b16b0c16dd9df5354880c8d9b901a0680abfadd 100644
--- a/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php
+++ b/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php
@@ -666,11 +666,11 @@ class SolrDefault extends AbstractBase
     }
 
     /**
-     * Support method for getOpenURL() -- pick the OpenURL format.
+     * Support method for getOpenUrl() -- pick the OpenURL format.
      *
      * @return string
      */
-    protected function getOpenURLFormat()
+    protected function getOpenUrlFormat()
     {
         // If we have multiple formats, Book, Journal and Article are most
         // important...
@@ -685,8 +685,10 @@ class SolrDefault extends AbstractBase
             return $formats[0];
         } else if (strlen($this->getCleanISSN()) > 0) {
             return 'Journal';
+        } else if (strlen($this->getCleanISBN()) > 0) {
+            return 'Book';
         }
-        return 'Book';
+        return 'UnknownFormat';
     }
 
     /**
@@ -717,7 +719,7 @@ class SolrDefault extends AbstractBase
      *
      * @return array
      */
-    protected function getDefaultOpenURLParams()
+    protected function getDefaultOpenUrlParams()
     {
         // Get a representative publication date:
         $pubDate = $this->getPublicationDates();
@@ -739,9 +741,9 @@ class SolrDefault extends AbstractBase
      *
      * @return array
      */
-    protected function getBookOpenURLParams()
+    protected function getBookOpenUrlParams()
     {
-        $params = $this->getDefaultOpenURLParams();
+        $params = $this->getDefaultOpenUrlParams();
         $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:book';
         $params['rft.genre'] = 'book';
         $params['rft.btitle'] = $params['rft.title'];
@@ -766,9 +768,9 @@ class SolrDefault extends AbstractBase
      *
      * @return array
      */
-    protected function getArticleOpenURLParams()
+    protected function getArticleOpenUrlParams()
     {
-        $params = $this->getDefaultOpenURLParams();
+        $params = $this->getDefaultOpenUrlParams();
         $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:journal';
         $params['rft.genre'] = 'article';
         $params['rft.issn'] = (string)$this->getCleanISSN();
@@ -798,9 +800,9 @@ class SolrDefault extends AbstractBase
      *
      * @return array
      */
-    protected function getUnknownFormatOpenURLParams($format)
+    protected function getUnknownFormatOpenUrlParams($format)
     {
-        $params = $this->getDefaultOpenURLParams();
+        $params = $this->getDefaultOpenUrlParams();
         $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:dc';
         $params['rft.creator'] = $this->getPrimaryAuthor();
         $publishers = $this->getPublishers();
@@ -820,9 +822,9 @@ class SolrDefault extends AbstractBase
      *
      * @return array
      */
-    protected function getJournalOpenURLParams()
+    protected function getJournalOpenUrlParams()
     {
-        $params = $this->getUnknownFormatOpenURLParams('Journal');
+        $params = $this->getUnknownFormatOpenUrlParams('Journal');
         /* This is probably the most technically correct way to represent
          * a journal run as an OpenURL; however, it doesn't work well with
          * Zotero, so it is currently commented out -- instead, we just add
@@ -855,23 +857,42 @@ class SolrDefault extends AbstractBase
      * Get the OpenURL parameters to represent this record (useful for the
      * title attribute of a COinS span tag).
      *
+     * @param bool $overrideSupportsOpenUrl Flag to override checking
+     * supportsOpenUrl() (default is false)
+     *
      * @return string OpenURL parameters.
      */
-    public function getOpenURL()
+    public function getOpenUrl($overrideSupportsOpenUrl = false)
     {
+        // stop here if this record does not support OpenURLs
+        if (!$overrideSupportsOpenUrl && !$this->supportsOpenUrl()) {
+            return false;
+        }
+
         // Set up parameters based on the format of the record:
-        $format = $this->getOpenURLFormat();
-        $method = "get{$format}OpenURLParams";
+        $format = $this->getOpenUrlFormat();
+        $method = "get{$format}OpenUrlParams";
         if (method_exists($this, $method)) {
             $params = $this->$method();
         } else {
-            $params = $this->getUnknownFormatOpenURLParams($format);
+            $params = $this->getUnknownFormatOpenUrlParams($format);
         }
 
         // Assemble the URL:
         return http_build_query($params);
     }
 
+    /**
+     * Get the OpenURL parameters to represent this record for COinS even if
+     * supportsOpenUrl() is false for this RecordDriver.
+     *
+     * @return string OpenURL parameters.
+     */
+    public function getCoinsOpenUrl()
+    {
+        return $this->getOpenUrl($this->supportsCoinsOpenUrl());
+    }
+
     /**
      * Get an array of physical descriptions of the item.
      *
@@ -1549,24 +1570,6 @@ class SolrDefault extends AbstractBase
         return false;
     }
 
-    /**
-     * Does the OpenURL configuration indicate that we should display OpenURLs in
-     * the specified context?
-     *
-     * @param string $area 'results', 'record' or 'holdings'
-     *
-     * @return bool
-     */
-    public function openURLActive($area)
-    {
-        // Only display OpenURL link if the option is turned on and we have
-        // an ISSN.  We may eventually want to make this rule more flexible.
-        if (!$this->getCleanISSN()) {
-            return false;
-        }
-        return parent::openURLActive($area);
-    }
-
     /**
      * Get an array of strings representing citation formats supported
      * by this record's data (empty if none).  For possible legal values,
diff --git a/module/VuFind/src/VuFind/RecordDriver/Summon.php b/module/VuFind/src/VuFind/RecordDriver/Summon.php
index 7eb20571b52c0a5ec041c1f76e163272bfd394ec..4b027d5dacca50881478a7e8dc5cdb438e97d07d 100644
--- a/module/VuFind/src/VuFind/RecordDriver/Summon.php
+++ b/module/VuFind/src/VuFind/RecordDriver/Summon.php
@@ -244,12 +244,32 @@ class Summon extends SolrDefault
      * Get the OpenURL parameters to represent this record (useful for the
      * title attribute of a COinS span tag).
      *
+     * @param bool $overrideSupportsOpenUrl Flag to override checking
+     * supportsOpenUrl() (default is false)
+     *
      * @return string OpenURL parameters.
      */
-    public function getOpenURL()
+    public function getOpenUrl($overrideSupportsOpenUrl = false)
     {
+        // stop here if this record does not support OpenURLs
+        if (!$overrideSupportsOpenUrl && !$this->supportsOpenUrl()) {
+            return false;
+        }
+
         return isset($this->fields['openUrl'])
-            ? $this->fields['openUrl'] : parent::getOpenURL();
+            ? $this->fields['openUrl']
+            : parent::getOpenUrl($overrideSupportsOpenUrl);
+    }
+
+    /**
+     * Checks the current record if it's supported for generating OpenURLs.
+     *
+     * @return bool
+     */
+    public function supportsOpenUrl()
+    {
+        // Summon never uses OpenURLs for anything other than COinS:
+        return false;
     }
 
     /**
@@ -615,18 +635,4 @@ class Summon extends SolrDefault
         }
         return $str;
     }
-
-    /**
-     * Does the OpenURL configuration indicate that we should display OpenURLs in
-     * the specified context?
-     *
-     * @param string $area 'results', 'record' or 'holdings'
-     *
-     * @return bool
-     */
-    public function openURLActive($area)
-    {
-        // Summon never uses OpenURLs for anything other than COinS:
-        return false;
-    }
 }
diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Factory.php b/module/VuFind/src/VuFind/View/Helper/Root/Factory.php
index 9f59e62c02a5f887e9e44fbb4efc506e4e077414..c6d56cc5feaadbc09d935230588a24003ceb4a35 100644
--- a/module/VuFind/src/VuFind/View/Helper/Root/Factory.php
+++ b/module/VuFind/src/VuFind/View/Helper/Root/Factory.php
@@ -316,8 +316,16 @@ class Factory
     public static function getOpenUrl(ServiceManager $sm)
     {
         $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config');
+        $openUrlRules = json_decode(
+            file_get_contents(
+                \VuFind\Config\Locator::getConfigPath('OpenUrlRules.json')
+            ),
+            true
+        );
         return new OpenUrl(
-            $sm->get('context'), isset($config->OpenURL) ? $config->OpenURL : null
+            $sm->get('context'),
+            $openUrlRules,
+            isset($config->OpenURL) ? $config->OpenURL : null
         );
     }
 
diff --git a/module/VuFind/src/VuFind/View/Helper/Root/OpenUrl.php b/module/VuFind/src/VuFind/View/Helper/Root/OpenUrl.php
index b2f5a5ae85ee572f90c3930afd0d26bc5fd170ce..0fbfbb776020b87e1ce7bbfd970249b64460a4fb 100644
--- a/module/VuFind/src/VuFind/View/Helper/Root/OpenUrl.php
+++ b/module/VuFind/src/VuFind/View/Helper/Root/OpenUrl.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * OpenURL view helper
+ * OpenUrl view helper
  *
  * PHP version 5
  *
@@ -28,7 +28,7 @@
 namespace VuFind\View\Helper\Root;
 
 /**
- * OpenURL view helper
+ * OpenUrl view helper
  *
  * @category VuFind2
  * @package  View_Helpers
@@ -52,27 +52,54 @@ class OpenUrl extends \Zend\View\Helper\AbstractHelper
      */
     protected $config;
 
+    /**
+     * OpenURL rules
+     *
+     * @var array
+     */
+    protected $openUrlRules;
+
+    /**
+     * Current RecordDriver
+     *
+     * @var \VuFind\RecordDriver
+     */
+    protected $recordDriver;
+
     /**
      * Constructor
      *
-     * @param \VuFind\View\Helper\Root\Context $context Context helper
-     * @param \Zend\Config\Config              $config  VuFind OpenURL configuration
+     * @param \VuFind\View\Helper\Root\Context $context      Context helper
+     * @param array                            $openUrlRules VuFind OpenURL rules
+     * @param \Zend\Config\Config              $config       VuFind OpenURL config
      */
     public function __construct(\VuFind\View\Helper\Root\Context $context,
-        $config = null
+        $openUrlRules, $config = null
     ) {
         $this->context = $context;
+        $this->openUrlRules = $openUrlRules;
         $this->config = $config;
     }
 
     /**
      * Render appropriate UI controls for an OpenURL link.
      *
-     * @param string $openUrl The OpenURL to display
+     * @param \VuFind\RecordDriver $driver The current recorddriver
+     *
+     * @return object
+     */
+    public function __invoke($driver)
+    {
+        $this->recordDriver = $driver;
+        return $this;
+    }
+
+    /**
+     * Public method to render the OpenURL template
      *
      * @return string
      */
-    public function __invoke($openUrl)
+    public function renderTemplate()
     {
         // Static counter to ensure that each OpenURL gets a unique ID.
         static $counter = 0;
@@ -92,7 +119,7 @@ class OpenUrl extends \Zend\View\Helper\AbstractHelper
 
         // Build parameters needed to display the control:
         $params = [
-            'openUrl' => $openUrl,
+            'openUrl' => $this->recordDriver->getOpenUrl(),
             'openUrlBase' => empty($base) ? false : $base,
             'openUrlWindow' => empty($this->config->window_settings)
                 ? false : $this->config->window_settings,
@@ -111,4 +138,181 @@ class OpenUrl extends \Zend\View\Helper\AbstractHelper
             'Helpers/openurl.phtml', $params
         );
     }
-}
\ No newline at end of file
+
+    /**
+     * Public method to check whether OpenURLs are active for current record
+     *
+     * @param string $area 'results', 'record' or 'holdings'
+     *
+     * @return bool
+     */
+    public function isActive($area)
+    {
+        // check first if OpenURLs are enabled for this RecordDriver
+        // check second if OpenURLs are enabled for this context
+        // check last if any rules apply
+        if (!$this->recordDriver->getOpenUrl()
+            || !$this->checkContext($area)
+            || !$this->checkIfRulesApply()
+        ) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Does the OpenURL configuration indicate that we should display OpenURLs in
+     * the specified context?
+     *
+     * @param string $area 'results', 'record' or 'holdings'
+     *
+     * @return bool
+     */
+    protected function checkContext($area)
+    {
+        // Doesn't matter the target area if no OpenURL resolver is specified:
+        if (!isset($this->config->url)) {
+            return false;
+        }
+
+        // If a setting exists, return that:
+        $key = 'show_in_' . $area;
+        if (isset($this->config->$key)) {
+            return $this->config->$key;
+        }
+
+        // If we got this far, use the defaults -- true for results, false for
+        // everywhere else.
+        return ($area == 'results');
+    }
+
+    /**
+     * Check if the rulesets found apply to the current record. First match counts.
+     *
+     * @return bool
+     */
+    protected function checkIfRulesApply()
+    {
+        foreach ($this->openUrlRules as $rules) {
+            if (!$this->checkExcludedRecordsRules($rules)
+                && $this->checkSupportedRecordsRules($rules)
+            ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if "exclude" rules from the OpenUrlRules.json file apply to
+     * the current record
+     *
+     * @param array $resolverDriverRules Array of rules for a specific resolverDriver
+     *
+     * @return bool
+     */
+    protected function checkExcludedRecordsRules($resolverDriverRules)
+    {
+        if (isset($resolverDriverRules['exclude'])) {
+            // No exclusion rules mean no exclusions -- return false
+            return count($resolverDriverRules['exclude'])
+                ? $this->checkRules($resolverDriverRules['exclude']) : false;
+        }
+        return false;
+    }
+
+    /**
+     * Check if "include" rules from the OpenUrlRules.json file apply to
+     * the current record
+     *
+     * @param array $resolverDriverRules Array of rules for a specific resolverDriver
+     *
+     * @return bool
+     */
+    protected function checkSupportedRecordsRules($resolverDriverRules)
+    {
+        if (isset($resolverDriverRules['include'])) {
+            // No inclusion rules mean include everything -- return true
+            return count($resolverDriverRules['include'])
+                ? $this->checkRules($resolverDriverRules['include']) : true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if method rules match.
+     *
+     * @param array $rules Rules to check.
+     *
+     * @return bool
+     */
+    protected function checkMethodRules($rules)
+    {
+        $ruleMatchCounter = 0;
+        foreach ($rules as $key => $value) {
+            if (is_callable([$this->recordDriver, $key])) {
+                $value = (array)$value;
+                $recordValue = (array)$this->recordDriver->$key();
+
+                if (in_array('*', $value)) {
+                    // wildcard present
+                    if (!count(
+                        array_diff(
+                            ['*'],
+                            array_diff($value, $recordValue)
+                        )
+                    )) {
+                        // if explicit defined values existed along with
+                        // wildcard those all also existed in recordValue
+                        $ruleMatchCounter++;
+                    }
+                } else {
+                    $valueCount = count($value);
+                    if ($valueCount == count($recordValue)
+                        && $valueCount == count(
+                            array_intersect($value, $recordValue)
+                        )
+                    ) {
+                        $ruleMatchCounter++;
+                    }
+                }
+            }
+        }
+
+        // Did all the rules match?
+        return ($ruleMatchCounter == count($rules));
+    }
+
+    /**
+     * Checks if rules from the OpenUrlRules.json file apply to the current
+     * record
+     *
+     * @param array $ruleset Array of rules to be checked
+     *
+     * @return bool
+     */
+    protected function checkRules($ruleset)
+    {
+        // check each rule - first rule-match
+        foreach ($ruleset as $rule) {
+            // skip this rule if it's not relevant for the current RecordDriver
+            if (isset($rule['recorddriver'])
+                && !($this->recordDriver instanceof $rule['recorddriver'])
+            ) {
+                continue;
+            }
+
+            // check if defined methods-rules apply for current record
+            if (isset($rule['methods'])) {
+                if ($this->checkMethodRules($rule['methods'])) {
+                    return true;
+                }
+            } else {
+                // no method rules? Then assume a match by default!
+                return true;
+            }
+        }
+        // no rule matched
+        return false;
+    }
+}
diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Record.php b/module/VuFind/src/VuFind/View/Helper/Root/Record.php
index 52a060bf793486f703bf3f058d51dffb8d11fbc9..5c7e38fd525284414b004245ee752ad18d321c90 100644
--- a/module/VuFind/src/VuFind/View/Helper/Root/Record.php
+++ b/module/VuFind/src/VuFind/View/Helper/Root/Record.php
@@ -600,4 +600,21 @@ class Record extends AbstractHelper
 
         return array_map($formatLink, $urls);
     }
+
+    /**
+     * Get all the links associated with this record depending on the OpenURL setting
+     * replace_other_urls.  Returns an array of associative arrays each containing
+     * 'desc' and 'url' keys.
+     *
+     * @return array
+     */
+    public function getLinkDetailsForOpenUrl()
+    {
+        if (isset($this->config->OpenURL->replace_other_urls)
+            && $this->config->OpenURL->replace_other_urls
+        ) {
+            return [];
+        }
+        return $this->getLinkDetails();
+    }
 }
diff --git a/module/VuFind/tests/fixtures/openurlrules/rule1.json b/module/VuFind/tests/fixtures/openurlrules/rule1.json
new file mode 100644
index 0000000000000000000000000000000000000000..032b3fcf3302a37ad1f39d768cfc7542d5d19db1
--- /dev/null
+++ b/module/VuFind/tests/fixtures/openurlrules/rule1.json
@@ -0,0 +1,21 @@
+[
+  {
+    "exclude" :
+    [
+      {
+        "recorddriver": "VuFind\\RecordDriver\\SolrMarc",
+        "methods": {
+          "getFormats": ["Article"]
+        }
+      }
+    ],
+    "include" :
+    [
+      {
+        "methods": {
+          "getFormats": ["Article", "*"]
+        }
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/openurlrules/rule2.json b/module/VuFind/tests/fixtures/openurlrules/rule2.json
new file mode 100644
index 0000000000000000000000000000000000000000..10b8755fa8d1c01648aaa44687e03ac34c7b8d33
--- /dev/null
+++ b/module/VuFind/tests/fixtures/openurlrules/rule2.json
@@ -0,0 +1,26 @@
+[
+  {
+    "exclude" :
+    [
+      {
+        "recorddriver": "VuFind\\RecordDriver\\SolrMarc",
+        "methods": {
+          "getFormats": ["Article"]
+        }
+      },
+      {
+        "methods": {
+          "getFormats": ["Article", "*"]
+        }
+      }
+    ],
+    "include" :
+    [
+      {
+        "methods": {
+          "getFormats": ["Article"]
+        }
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/openurlrules/rule3.json b/module/VuFind/tests/fixtures/openurlrules/rule3.json
new file mode 100644
index 0000000000000000000000000000000000000000..383b644fafc28b0194c1276c12318b2a31feb441
--- /dev/null
+++ b/module/VuFind/tests/fixtures/openurlrules/rule3.json
@@ -0,0 +1,13 @@
+[
+  {
+    "include" :
+    [
+      {
+        "methods": {
+          "getCleanISSN": "*",
+          "getFormats": ["Article"]
+        }
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/openurlrules/rule4.json b/module/VuFind/tests/fixtures/openurlrules/rule4.json
new file mode 100644
index 0000000000000000000000000000000000000000..d11ba86835d8af5804788d83869e59ed98495cf0
--- /dev/null
+++ b/module/VuFind/tests/fixtures/openurlrules/rule4.json
@@ -0,0 +1,21 @@
+[
+  {
+    "include": [
+      {
+        "methods": {
+          "getCleanISSN": "*",
+          "getFormats": ["Journal"]
+        }
+      },
+      {
+        "methods": {
+          "getCleanISSN": "*",
+          "getFormats": [
+            "Article",
+            "ElectronicArticle"
+          ]
+        }
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrDefaultTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrDefaultTest.php
index 69c2265617210956b9da76c5ce69307dea711e4a..642c0622710cba0532b12d06ed48be1b2f0cdfe1 100644
--- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrDefaultTest.php
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/SolrDefaultTest.php
@@ -50,7 +50,7 @@ class SolrDefaultTest extends \VuFindTest\Unit\TestCase
     public function testBookOpenURL()
     {
         $driver = $this->getDriver();
-        $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.title=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.date=1992&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&rft.genre=book&rft.btitle=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.series=Vico%2C+Giambattista%2C+1668-1744.+Works.+1982+%3B&rft.au=Vico%2C+Giambattista%2C+1668-1744.&rft.pub=Centro+di+Studi+Vichiani%2C&rft.edition=Fictional+edition.&rft.isbn=8820737493', $driver->getOpenURL());
+        $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.title=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.date=1992&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&rft.genre=book&rft.btitle=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.series=Vico%2C+Giambattista%2C+1668-1744.+Works.+1982+%3B&rft.au=Vico%2C+Giambattista%2C+1668-1744.&rft.pub=Centro+di+Studi+Vichiani%2C&rft.edition=Fictional+edition.&rft.isbn=8820737493', $driver->getOpenUrl());
     }
 
     /**
@@ -68,7 +68,7 @@ class SolrDefaultTest extends \VuFindTest\Unit\TestCase
             'container_start_page' => '12',
         ];
         $driver = $this->getDriver($overrides);
-        $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.date=1992&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&rft.genre=article&rft.issn=&rft.isbn=8820737493&rft.volume=XVII&rft.issue=6&rft.spage=12&rft.jtitle=Fake+Container&rft.atitle=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.au=Vico%2C+Giambattista%2C+1668-1744.&rft.format=Article&rft.language=Italian', $driver->getOpenURL());
+        $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.date=1992&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&rft.genre=article&rft.issn=&rft.isbn=8820737493&rft.volume=XVII&rft.issue=6&rft.spage=12&rft.jtitle=Fake+Container&rft.atitle=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.au=Vico%2C+Giambattista%2C+1668-1744.&rft.format=Article&rft.language=Italian', $driver->getOpenUrl());
     }
 
     /**
@@ -83,7 +83,7 @@ class SolrDefaultTest extends \VuFindTest\Unit\TestCase
             'issn' => ['1234-5678'],
         ];
         $driver = $this->getDriver($overrides);
-        $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.title=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc&rft.creator=Vico%2C+Giambattista%2C+1668-1744.&rft.pub=Centro+di+Studi+Vichiani%2C&rft.format=Journal&rft.language=Italian&rft.issn=1234-5678', $driver->getOpenURL());
+        $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.title=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc&rft.creator=Vico%2C+Giambattista%2C+1668-1744.&rft.pub=Centro+di+Studi+Vichiani%2C&rft.format=Journal&rft.language=Italian&rft.issn=1234-5678', $driver->getOpenUrl());
     }
 
     /**
@@ -97,7 +97,7 @@ class SolrDefaultTest extends \VuFindTest\Unit\TestCase
             'format' => ['Thingie'],
         ];
         $driver = $this->getDriver($overrides);
-        $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.title=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.date=1992&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc&rft.creator=Vico%2C+Giambattista%2C+1668-1744.&rft.pub=Centro+di+Studi+Vichiani%2C&rft.format=Thingie&rft.language=Italian', $driver->getOpenURL());
+        $this->assertEquals('url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator&rft.title=La+congiura+dei+Principi+Napoletani+1701+%3A+%28prima+e+seconda+stesura%29+%2F&rft.date=1992&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc&rft.creator=Vico%2C+Giambattista%2C+1668-1744.&rft.pub=Centro+di+Studi+Vichiani%2C&rft.format=Thingie&rft.language=Italian', $driver->getOpenUrl());
     }
 
     /**
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..665baa6e962a1d050ca4c56acc13064de3422e4d
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php
@@ -0,0 +1,258 @@
+<?php
+/**
+ * OpenUrl Test Class
+ *
+ * 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  Tests
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @author   André Lahmann <lahmann@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/vufind2:unit_tests Wiki
+ */
+namespace VuFindTest\View\Helper\Root;
+use VuFind\View\Helper\Root\OpenUrl, Zend\Config\Config, InvalidArgumentException;
+
+/**
+ * OpenUrl Test Class
+ *
+ * @category VuFind2
+ * @package  Tests
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @author   André Lahmann <lahmann@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/vufind2:unit_tests Wiki
+ */
+class OpenUrlTest extends \VuFindTest\Unit\ViewHelperTestCase
+{
+    /**
+     * Configuration array providing basic settings for testing OpenUrlRules
+     *
+     * @var array
+     */
+    protected $rulesConfig = ['url' => 'http://foo/bar', 'show_in_results' => true];
+
+    /**
+     * Test checkContext() default behavior.
+     *
+     * @return void
+     */
+    public function testCheckContextDefaults()
+    {
+        $config = [
+            'url' => 'http://foo/bar'
+        ];
+        $openUrl = $this->getOpenUrl(null, $config)
+            ->__invoke($this->getMockDriver());
+        $this->assertTrue($openUrl->isActive('results'));
+        $this->assertFalse($openUrl->isActive('foo'));
+    }
+
+    /**
+     * Test checkContext() behavior with configuration overrides.
+     *
+     * @return void
+     */
+    public function testCheckContextWithOverrides()
+    {
+        $config = [
+            'url' => 'http://foo/bar',
+            'show_in_results' => false,
+            'show_in_foo' => true,
+        ];
+        $openUrl = $this->getOpenUrl(null, $config)
+            ->__invoke($this->getMockDriver());
+        $this->assertFalse($openUrl->isActive('results'));
+        $this->assertTrue($openUrl->isActive('foo'));
+    }
+
+    /**
+     * Test checkContext() with no URL set (everything should be false!)
+     *
+     * @return void
+     */
+    public function testCheckContextNoUrl()
+    {
+        $openUrl = $this->getOpenUrl()
+            ->__invoke($this->getMockDriver());
+        $this->assertFalse($openUrl->isActive('results'));
+        $this->assertFalse($openUrl->isActive('foo'));
+    }
+
+    /**
+     * Test checkExcludedRecordRules() with rule not applying (isActive() will return
+     * TRUE!!)
+     *
+     * @return void
+     */
+    public function testCheckExcludedRecordsRulesFalse()
+    {
+        $openUrl = $this
+            ->getOpenUrl($this->getFixture("rule1.json"), $this->rulesConfig)
+            ->__invoke($this->getMockDriver());
+        $this->assertTrue($openUrl->isActive('results'));
+    }
+
+    /**
+     * Test checkExcludedRecordRules() with matching rule (isActive() will return
+     * FALSE!!)
+     *
+     * @return void
+     */
+    public function testCheckExcludedRecordsRulesTrue()
+    {
+        $openUrl = $this
+            ->getOpenUrl($this->getFixture("rule2.json"), $this->rulesConfig)
+            ->__invoke($this->getMockDriver());
+        $this->assertFalse($openUrl->isActive('results'));
+    }
+
+    /**
+     * Test checkSupportedRecordRules() with no matching rule (isActive() will return
+     * FALSE!!)
+     *
+     * @return void
+     */
+    public function testCheckSupportedRecordsRulesFalse()
+    {
+        $openUrl = $this
+            ->getOpenUrl($this->getFixture("rule3.json"), $this->rulesConfig)
+            ->__invoke($this->getMockDriver());
+        $this->assertFalse($openUrl->isActive('results'));
+    }
+
+    /**
+     * Test checkSupportedRecordRules() with matching rule (isActive() will return
+     * TRUE!!)
+     *
+     * @return void
+     */
+    public function testCheckSupportedRecordsRulesTrue()
+    {
+        $openUrl = $this
+            ->getOpenUrl($this->getFixture("rule4.json"), $this->rulesConfig)
+            ->__invoke($this->getMockDriver());
+        $this->assertTrue($openUrl->isActive('results'));
+    }
+
+    /**
+     * Test checkSupportedRecordRules() to see if it accounts for record driver
+     * class.
+     *
+     * @return void
+     */
+    public function testRecordDriverClassInRules()
+    {
+        $formats = ['Article'];
+        $defaultDriver = $this->getMockDriver(
+            'fake-data', 'VuFind\RecordDriver\SolrDefault', $formats
+        );
+        $marcDriver = $this->getMockDriver(
+            'fake-data', 'VuFind\RecordDriver\SolrMarc', $formats
+        );
+        $openUrl = $this
+            ->getOpenUrl($this->getFixture("rule1.json"), $this->rulesConfig);
+        $this->assertTrue($openUrl->__invoke($defaultDriver)->isActive('results'));
+        $this->assertFalse($openUrl->__invoke($marcDriver)->isActive('results'));
+    }
+
+    /**
+     * Get mock context helper.
+     *
+     * @return \VuFind\View\Helper\Root\Context
+     */
+    protected function getMockContext()
+    {
+        return $this->getMockBuilder('VuFind\View\Helper\Root\Context')
+            ->disableOriginalConstructor()->getMock();
+    }
+
+    /**
+     * Get mock driver that returns an openURL.
+     *
+     * @param string $openUrl OpenURL to return
+     * @param string $class   Class to mock
+     * @param array  $formats Formats to return from getFormats
+     *
+     * @return \VuFind\RecordDriver\SolrDefault
+     */
+    protected function getMockDriver($openUrl = 'fake-data',
+        $class = 'VuFind\RecordDriver\SolrDefault',
+        $formats = ['ElectronicArticle', 'Article']
+    ) {
+        $driver = $this->getMockBuilder($class)
+            ->disableOriginalConstructor()->getMock();
+        $driver->expects($this->any())->method('getOpenUrl')
+            ->will($this->returnValue($openUrl));
+        $driver->expects($this->any())->method('getCleanISSN')
+            ->will($this->returnValue('1234-5678'));
+        $driver->expects($this->any())->method('getFormats')
+            ->will($this->returnValue($formats));
+        return $driver;
+    }
+
+    /**
+     * Get the fixtures for testing OpenUrlRules
+     *
+     * @param string $fixture filename of the fixture to load
+     *
+     * @return mixed|null
+     */
+    protected function getFixture($fixture)
+    {
+        if ($fixture) {
+            $file = realpath(
+                __DIR__ .
+                '/../../../../../../../tests/fixtures/openurlrules/' . $fixture
+            );
+            if (!is_string($file) || !file_exists($file) || !is_readable($file)) {
+                throw new \InvalidArgumentException(
+                    sprintf('Unable to load fixture file: %s ', $fixture)
+                );
+            }
+            return json_decode(file_get_contents($file), true);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the object to test
+     *
+     * @param array  $rules       JSON-decoded array containing rules (optional)
+     * @param array  $config      Configuration settings (optional)
+     * @param object $mockContext Mock context helper (optional)
+     *
+     * @return OpenURL
+     */
+    protected function getOpenUrl($rules = null, $config = [], $mockContext = null)
+    {
+        if (null === $rules) {
+            $json = __DIR__
+                . '/../../../../../../../../../config/vufind/OpenUrlRules.json';
+            $rules = json_decode(file_get_contents($json), true);
+        }
+        if (null === $mockContext) {
+            $mockContext = $this->getMockContext();
+        }
+        $openUrl = new OpenUrl($mockContext, $rules, new Config($config));
+        $openUrl->setView($this->getPhpRenderer());
+        return $openUrl;
+    }
+}
\ No newline at end of file
diff --git a/themes/blueprint/templates/RecordDriver/Pazpar2/result-list.phtml b/themes/blueprint/templates/RecordDriver/Pazpar2/result-list.phtml
index 43d379170df2dc5ef4b19c31d7bddeee716146cf..22b08523b7796cc23c7daa616aa0412d72302ddd 100644
--- a/themes/blueprint/templates/RecordDriver/Pazpar2/result-list.phtml
+++ b/themes/blueprint/templates/RecordDriver/Pazpar2/result-list.phtml
@@ -56,18 +56,18 @@
           <? endif; ?>
       </div>
 
-      <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
-            but even if we don't plan to display the link, we still want to get the $openUrl
-            value for use in generating a COinS (Z3988) tag -- see bottom of file.
-          */
-         $openUrl = $this->driver->getOpenURL();
-         $openUrlActive = $this->driver->openURLActive('results');
-         $urls = $this->record($this->driver)->getLinkDetails();
-         if ($openUrlActive || !empty($urls)): ?>
+        <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
+              but even if we don't plan to display the link, we still want to get the $openUrl
+              value for use in generating a COinS (Z3988) tag -- see bottom of file.
+            */
+        $openUrl = $this->openUrl($this->driver);
+        $openUrlActive = $openUrl->isActive('results');
+        // Account for replace_other_urls setting
+        $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
+        if ($openUrlActive || !empty($urls)): ?>
         <? if ($openUrlActive): ?>
           <br/>
-          <?=$this->openUrl($openUrl)?>
-          <? if ($this->driver->replaceURLsWithOpenURL()) $urls = array(); // clear URL list if replace setting is active ?>
+          <?=$openUrl->renderTemplate()?>
         <? endif; ?>
         <? if (!is_array($urls)) $urls = array(); foreach ($urls as $current): ?>
           <br/>
@@ -88,4 +88,4 @@
   <div class="clear"></div>
 </div>
 
-<?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?>
+<?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?>
diff --git a/themes/blueprint/templates/RecordDriver/SolrDefault/collection-info.phtml b/themes/blueprint/templates/RecordDriver/SolrDefault/collection-info.phtml
index b454edf2ca9c70f2ae0a23f519889087d5ba1dd8..04843bd0ab8020824856434c973b3c6dcc7d438a 100644
--- a/themes/blueprint/templates/RecordDriver/SolrDefault/collection-info.phtml
+++ b/themes/blueprint/templates/RecordDriver/SolrDefault/collection-info.phtml
@@ -130,19 +130,20 @@
   <? endif; ?>
 
   <?
-      $openUrl = $this->driver->openURLActive('record') ? $this->driver->getOpenURL() : false;
-      // Account for replace_other_urls setting
-      $urls = ($openUrl && $this->driver->replaceURLsWithOpenURL()) ? array() : $this->record($this->driver)->getLinkDetails();
+    $openUrl = $this->openUrl($this->driver);
+    $openUrlActive = $openUrl->isActive('record');
+    // Account for replace_other_urls setting
+    $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
   ?>
-  <? if (!empty($urls) || $openUrl): ?>
+  <? if (!empty($urls) || $openUrlActive): ?>
   <tr valign="top">
     <th><?=$this->transEsc('Online Access')?>: </th>
     <td>
       <? foreach ($urls as $current): ?>
         <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/>
       <? endforeach; ?>
-      <? if ($openUrl): ?>
-        <?=$this->openUrl($openUrl)?><br/>
+      <? if ($openUrlActive): ?>
+        <?=$openUrl->renderTemplate()?><br/>
       <? endif; ?>
     </td>
   </tr>
diff --git a/themes/blueprint/templates/RecordDriver/SolrDefault/core.phtml b/themes/blueprint/templates/RecordDriver/SolrDefault/core.phtml
index b407f5af3369048f6396610a1f5ae4a16352e874..e7977746ef9f27faff45d43332bf6d3094898cce 100644
--- a/themes/blueprint/templates/RecordDriver/SolrDefault/core.phtml
+++ b/themes/blueprint/templates/RecordDriver/SolrDefault/core.phtml
@@ -171,19 +171,20 @@
     <? endif; ?>
 
     <?
-        $openUrl = $this->driver->openURLActive('record') ? $this->driver->getOpenURL() : false;
-        // Account for replace_other_urls setting
-        $urls = ($openUrl && $this->driver->replaceURLsWithOpenURL()) ? array() : $this->record($this->driver)->getLinkDetails();
+      $openUrl = $this->openUrl($this->driver);
+      $openUrlActive = $openUrl->isActive('records');
+      // Account for replace_other_urls setting
+      $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
     ?>
-    <? if (!empty($urls) || $openUrl): ?>
+    <? if (!empty($urls) || $openUrlActive): ?>
     <tr valign="top">
       <th><?=$this->transEsc('Online Access')?>: </th>
       <td>
         <? foreach ($urls as $current): ?>
           <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/>
         <? endforeach; ?>
-        <? if ($openUrl): ?>
-          <?=$this->openUrl($openUrl)?><br/>
+        <? if ($openUrlActive): ?>
+          <?=$openUrl->renderTemplate()?><br/>
         <? endif; ?>
       </td>
     </tr>
diff --git a/themes/blueprint/templates/RecordDriver/SolrDefault/list-entry.phtml b/themes/blueprint/templates/RecordDriver/SolrDefault/list-entry.phtml
index 10946666978c46e50979b81c9bb8e03951063710..21666d56ede009a50cf4274e8fadb0f3f1a1c6c6 100644
--- a/themes/blueprint/templates/RecordDriver/SolrDefault/list-entry.phtml
+++ b/themes/blueprint/templates/RecordDriver/SolrDefault/list-entry.phtml
@@ -72,18 +72,18 @@
         <? endif; ?>
       <? endif; ?>
     </div>
-    <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
-          but even if we don't plan to display the link, we still want to get the $openUrl
-          value for use in generating a COinS (Z3988) tag -- see bottom of file.
-        */
-       $openUrl = $this->driver->getOpenURL();
-       $openUrlActive = $this->driver->openURLActive('results');
-       $urls = $this->record($this->driver)->getLinkDetails();
+    <?  /* We need to find out if we're supposed to display an OpenURL link ($openUrlTemplate),
+           but even if we don't plan to display the link, we still want to get the $openUrl
+           value for use in generating a COinS (Z3988) tag -- see bottom of file.
+         */
+       $openUrl = $this->openUrl($this->driver);
+       $openUrlActive = $openUrl->isActive('results');
+       // Account for replace_other_urls setting
+       $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
        if ($openUrlActive || !empty($urls)): ?>
       <? if ($openUrlActive): ?>
         <br/>
-        <?=$this->openUrl($openUrl)?>
-        <? if ($this->driver->replaceURLsWithOpenURL()) $urls = array(); // clear URL list if replace setting is active ?>
+        <?=$openUrl->renderTemplate()?>
       <? endif; ?>
       <? if (!is_array($urls)) $urls = array(); foreach ($urls as $current): ?>
         <br/>
@@ -117,4 +117,4 @@
   <div class="clear"></div>
 </div>
 
-<?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?>
+<?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?>
\ No newline at end of file
diff --git a/themes/blueprint/templates/RecordDriver/SolrDefault/result-grid.phtml b/themes/blueprint/templates/RecordDriver/SolrDefault/result-grid.phtml
index 9132bc85521a105a41c452f939caab13090c2212..6d07b79a5163fc275d52f4f8036a9a67d14d84c7 100644
--- a/themes/blueprint/templates/RecordDriver/SolrDefault/result-grid.phtml
+++ b/themes/blueprint/templates/RecordDriver/SolrDefault/result-grid.phtml
@@ -7,17 +7,19 @@
       <a class="gridTitle" href="<?=$this->recordLink()->getUrl($this->driver)?>">
         <?=$this->record($this->driver)->getTitleHtml(80)?>
       </a>
-      <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
-            but even if we don't plan to display the link, we still want to get the $openUrl
-            value for use in generating a COinS (Z3988) tag -- see bottom of file.
-          */
-         $openUrl = $this->driver->getOpenURL();
-         $openUrlActive = $this->driver->openURLActive('results');
-         $urls = $this->record($this->driver)->getLinkDetails();
+        <?
+        /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
+           but even if we don't plan to display the link, we still want to get the $openUrl
+           value for use in generating a COinS (Z3988) tag -- see bottom of file.
+        */
+        $openUrl = $this->openUrl($this->driver);
+        $openUrlActive = $openUrl->isActive('results');
+        // Account for replace_other_urls setting
+        $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
+        ?>
          if ($openUrlActive || !empty($urls)): ?>
         <? if ($openUrlActive): ?>
           <?=$this->openUrl($openUrl)?><br />
-          <? if ($this->driver->replaceURLsWithOpenURL()) $urls = array(); // clear URL list if replace setting is active ?>
         <? endif; ?>
         <? if (!is_array($urls)) $urls = array(); foreach ($urls as $current): ?>
           <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a>
@@ -31,4 +33,4 @@
     </div>
 </div>
 
-<?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?>
+<?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?>
diff --git a/themes/blueprint/templates/RecordDriver/SolrDefault/result-list.phtml b/themes/blueprint/templates/RecordDriver/SolrDefault/result-list.phtml
index a51f80d5e751e939e55544053a9ccf344c2c96b4..a0a952121c49f9690d60459642cd12121a86b5b0 100644
--- a/themes/blueprint/templates/RecordDriver/SolrDefault/result-list.phtml
+++ b/themes/blueprint/templates/RecordDriver/SolrDefault/result-list.phtml
@@ -105,18 +105,18 @@
         <? endif; ?>
       </div>
 
-      <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
+        <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlTemplate),
             but even if we don't plan to display the link, we still want to get the $openUrl
             value for use in generating a COinS (Z3988) tag -- see bottom of file.
           */
-         $openUrl = $this->driver->getOpenURL();
-         $openUrlActive = $this->driver->openURLActive('results');
-         $urls = $this->record($this->driver)->getLinkDetails();
+        $openUrl = $this->openUrl($this->driver);
+        $openUrlActive = $openUrl->isActive('results');
+        // Account for replace_other_urls setting
+        $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
          if ($openUrlActive || !empty($urls)): ?>
         <? if ($openUrlActive): ?>
           <br/>
-          <?=$this->openUrl($openUrl)?>
-          <? if ($this->driver->replaceURLsWithOpenURL()) $urls = array(); // clear URL list if replace setting is active ?>
+          <?=$openUrl->renderTemplate()?>
         <? endif; ?>
         <? if (!is_array($urls)) $urls = array(); foreach ($urls as $current): ?>
           <br/>
@@ -174,4 +174,4 @@
   <div class="clear"></div>
 </div>
 
-<?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?>
+<?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?>
diff --git a/themes/blueprint/templates/RecordTab/holdingsils.phtml b/themes/blueprint/templates/RecordTab/holdingsils.phtml
index 62a20a9acc609c16625406bf2c67269f9a66e791..566eb49983ca5334c791dc3c97407ba024b2c7a5 100644
--- a/themes/blueprint/templates/RecordTab/holdingsils.phtml
+++ b/themes/blueprint/templates/RecordTab/holdingsils.phtml
@@ -3,11 +3,11 @@
     $account = $this->auth()->getManager();
     $user = $account->isLoggedIn();
     $holdings = $this->driver->getRealTimeHoldings();
-    $openUrl = $this->driver->openURLActive('holdings') ? $this->driver->getOpenURL() : false;
-    $offlineMode = $this->ils()->getOfflineMode();
+    $openUrl = $this->openUrl($this->driver);
+    $openUrlActive = $openUrl->isActive('holdings');
     // Account for replace_other_urls setting
-    $urls = ($openUrl && $this->driver->replaceURLsWithOpenURL()) ? array() : $this->record($this->driver)->getLinkDetails();
-
+    $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
+    $offlineMode = $this->ils()->getOfflineMode();
     // Set page title.
     $this->headTitle($this->translate('Holdings') . ': ' . $this->driver->getBreadcrumb());
 ?>
@@ -39,14 +39,14 @@
 <? $holdingTitleHold = $this->driver->tryMethod('getRealTimeTitleHold'); if (!empty($holdingTitleHold)): ?>
     <a class="holdPlace" href="<?=$this->recordLink()->getRequestUrl($holdingTitleHold)?>"><?=$this->transEsc('title_hold_place')?></a>
 <? endif; ?>
-<? if (!empty($urls) || $openUrl): ?>
+<? if (!empty($urls) || $openUrlActive): ?>
   <h3><?=$this->transEsc("Internet")?></h3>
   <? if (!empty($urls)): ?>
     <? foreach ($urls as $current): ?>
       <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/>
     <? endforeach; ?>
   <? endif; ?>
-  <? if ($openUrl): ?><?=$this->openUrl($openUrl);?><? endif; ?>
+  <? if ($openUrlActive): ?><?=$openUrl->renderTemplate()?><? endif; ?>
 <? endif; ?>
 <? foreach ($holdings as $holding): ?>
 <h3><?=$this->transEsc('location_' . $holding['location'], array(), $holding['location'])?></h3>
diff --git a/themes/blueprint/templates/collection/view.phtml b/themes/blueprint/templates/collection/view.phtml
index 7ec1cc2db6d16250ac36d3caf15838dba3f53b11..6b454c903d37edb20605594180606d1b3f6a8019 100644
--- a/themes/blueprint/templates/collection/view.phtml
+++ b/themes/blueprint/templates/collection/view.phtml
@@ -58,7 +58,7 @@
     <?=isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?>
   </div>
 
-  <span class="Z3988" title="<?=$this->escapeHtmlAttr($this->driver->getOpenURL())?>"></span>
+  <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenURL()).'"></span>':''?>
 </div>
 
 <? if (!$tree): ?>
diff --git a/themes/blueprint/templates/record/view.phtml b/themes/blueprint/templates/record/view.phtml
index 94e76fa86768ac7bc9b7afac0e211096b6e1b13b..0c147768ff7517ac3dd97fb5cd0603c7751b3492 100644
--- a/themes/blueprint/templates/record/view.phtml
+++ b/themes/blueprint/templates/record/view.phtml
@@ -58,7 +58,7 @@
     <?=isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?>
   </div>
 
-  <span class="Z3988" title="<?=$this->escapeHtmlAttr($this->driver->getOpenURL())?>"></span>
+  <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenURL()).'"></span>':''?>
 </div>
 
 <div class="<?=$this->layoutClass('sidebar')?>">
diff --git a/themes/bootstrap3/templates/RecordDriver/Pazpar2/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/Pazpar2/result-list.phtml
index ada12fcb6532e5f4a02890105a476ea0370d3c32..7ed216c6708aeb4f6f0da99efd648914d9fd668c 100644
--- a/themes/bootstrap3/templates/RecordDriver/Pazpar2/result-list.phtml
+++ b/themes/bootstrap3/templates/RecordDriver/Pazpar2/result-list.phtml
@@ -75,14 +75,14 @@
               but even if we don't plan to display the link, we still want to get the $openUrl
               value for use in generating a COinS (Z3988) tag -- see bottom of file.
             */
-          $openUrl = $this->driver->getOpenURL();
-          $openUrlActive = $this->driver->openURLActive('results');
-          $urls = $this->record($this->driver)->getLinkDetails();
+          $openUrl = $this->openUrl($this->driver);
+          $openUrlActive = $openUrl->isActive('results');
+          // Account for replace_other_urls setting
+          $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
           if ($openUrlActive || !empty($urls)): ?>
           <? if ($openUrlActive): ?>
             <br/>
-            <?=$this->openUrl($openUrl)?>
-            <? if ($this->driver->replaceURLsWithOpenURL()) $urls = array(); // clear URL list if replace setting is active ?>
+            <?=$openUrl->renderTemplate()?>
           <? endif; ?>
           <? if (!is_array($urls)) $urls = array();
             if(!$this->driver->isCollection()):
@@ -101,7 +101,7 @@
         <? endif; ?>
         <?=$this->record($this->driver)->getPreviews()?>
 
-        <?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?>
+        <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?>
       </div>
     </div>
   </div>
diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-info.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-info.phtml
index da8a950737b46828e8f235d09c1fb9fc3f3a1485..7963132aa060c4e6f4565e2552107de8e0d8abd8 100644
--- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-info.phtml
+++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/collection-info.phtml
@@ -153,19 +153,20 @@
       <? endif; ?>
 
       <?
-          $openUrl = $this->driver->openURLActive('record') ? $this->driver->getOpenURL() : false;
-          // Account for replace_other_urls setting
-          $urls = ($openUrl && $this->driver->replaceURLsWithOpenURL()) ? array() : $this->record($this->driver)->getLinkDetails();
+        $openUrl = $this->openUrl($this->driver);
+        $openUrlActive = $openUrl->isActive('record');
+        // Account for replace_other_urls setting
+        $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
       ?>
-      <? if (!empty($urls) || $openUrl): ?>
+      <? if (!empty($urls) || $openUrlActive): ?>
       <tr>
         <th><?=$this->transEsc('Online Access')?>: </th>
         <td>
           <? foreach ($urls as $current): ?>
             <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/>
           <? endforeach; ?>
-          <? if ($openUrl): ?>
-            <?=$this->openUrl($openUrl)?><br/>
+          <? if ($openUrlActive): ?>
+            <?=$openUrl->renderTemplate()?><br/>
           <? endif; ?>
         </td>
       </tr>
diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml
index a57b12c62252e08e528d94e843d4f0402ba0084f..571ee7a3eecc5177b2a6f23026b13097056b3c16 100644
--- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml
+++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml
@@ -213,19 +213,20 @@
       <? endif; ?>
 
       <?
-        $openUrl = $this->driver->openURLActive('record') ? $this->driver->getOpenURL() : false;
+        $openUrl = $this->openUrl($this->driver);
+        $openUrlActive = $openUrl->isActive('records');
         // Account for replace_other_urls setting
-        $urls = ($openUrl && $this->driver->replaceURLsWithOpenURL()) ? array() : $this->record($this->driver)->getLinkDetails();
+        $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
       ?>
-      <? if (!empty($urls) || $openUrl): ?>
+      <? if (!empty($urls) || $openUrlActive): ?>
       <tr>
         <th><?=$this->transEsc('Online Access')?>: </th>
         <td>
           <? foreach ($urls as $current): ?>
             <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/>
           <? endforeach; ?>
-          <? if ($openUrl): ?>
-            <?=$this->openUrl($openUrl)?><br/>
+          <? if ($openUrlActive): ?>
+            <?=$openUrl->renderTemplate()?><br/>
           <? endif; ?>
         </td>
       </tr>
diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/list-entry.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/list-entry.phtml
index a353ae6b7c1ddd48569d5bcf96470fbf18aca385..5c6cdc18de8e39ca4d2549f9e7faa643836a1ccc 100644
--- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/list-entry.phtml
+++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/list-entry.phtml
@@ -123,24 +123,20 @@
         <? endif; ?>
       </div>
 
-      <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
+      <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlTemplate),
             but even if we don't plan to display the link, we still want to get the $openUrl
             value for use in generating a COinS (Z3988) tag -- see bottom of file.
           */
-        $openUrl = $this->driver->getOpenURL();
-        $openUrlActive = $this->driver->openURLActive('results');
-        $urls = $this->record($this->driver)->getLinkDetails();
+        $openUrl = $this->openUrl($this->driver);
+        $openUrlActive = $openUrl->isActive('results');
+        // Account for replace_other_urls setting
+        $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
+
         if ($openUrlActive || !empty($urls)):
       ?>
         <? if ($openUrlActive): ?>
           <br/>
-          <?=$this->openUrl($openUrl)?>
-          <?
-            if ($this->driver->replaceURLsWithOpenURL()) {
-              // clear URL list if replace setting is active
-              $urls = array();
-            }
-          ?>
+          <?=$openUrl->renderTemplate()?>
         <? endif;?>
 
         <? if (!is_array($urls)) { $urls = array(); }
@@ -181,6 +177,6 @@
       </ul>
     </div>
 
-    <?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?>
+    <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?>
   </div>
 </div>
diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-grid.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-grid.phtml
index fdb81db4ce96a536a0260418c301bdec8c66571d..ee78dde499e040cff71841dd7b7d8030d6a985fe 100644
--- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-grid.phtml
+++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-grid.phtml
@@ -3,9 +3,10 @@
    but even if we don't plan to display the link, we still want to get the $openUrl
    value for use in generating a COinS (Z3988) tag -- see bottom of file.
 */
-$openUrl = $this->driver->getOpenURL();
-$openUrlActive = $this->driver->openURLActive('results');
-$urls = $this->record($this->driver)->getLinkDetails();
+$openUrl = $this->openUrl($this->driver);
+$openUrlActive = $openUrl->isActive('results');
+// Account for replace_other_urls setting
+$urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
 ?>
 
 <div class="result <?=$this->driver->supportsAjaxStatus()?' ajaxItem':''?>">
@@ -29,8 +30,7 @@ $urls = $this->record($this->driver)->getLinkDetails();
     <? if ($openUrlActive || !empty($urls)): ?>
       <br/><br/>
       <? if ($openUrlActive): ?>
-        <?=$this->openUrl($openUrl)?><br />
-        <? if ($this->driver->replaceURLsWithOpenURL()) $urls = array(); // clear URL list if replace setting is active ?>
+        <?=$openUrl->renderTemplate()?><br />
       <? endif; ?>
       <? if (!is_array($urls)) $urls = array(); foreach ($urls as $current): ?>
         <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a>
@@ -40,4 +40,4 @@ $urls = $this->record($this->driver)->getLinkDetails();
   </div>
 </div>
 
-<?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?>
+<?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?>
diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-list.phtml
index eba0d573e09922f037dfcf30e162b139d8e7d17d..bb13f3d0010e3a5e770d2a60cfcbfd88f007e96c 100644
--- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-list.phtml
+++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/result-list.phtml
@@ -112,18 +112,19 @@
         <? endif; ?>
       </div>
 
-      <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
+      <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlTemplate),
             but even if we don't plan to display the link, we still want to get the $openUrl
             value for use in generating a COinS (Z3988) tag -- see bottom of file.
           */
-        $openUrl = $this->driver->getOpenURL();
-        $openUrlActive = $this->driver->openURLActive('results');
-        $urls = $this->record($this->driver)->getLinkDetails();
+        $openUrl = $this->openUrl($this->driver);
+        $openUrlActive = $openUrl->isActive('results');
+        // Account for replace_other_urls setting
+        $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
+
         if ($openUrlActive || !empty($urls)): ?>
         <? if ($openUrlActive): ?>
           <br/>
-          <?=$this->openUrl($openUrl)?>
-          <? if ($this->driver->replaceURLsWithOpenURL()) $urls = array(); // clear URL list if replace setting is active ?>
+          <?=$openUrl->renderTemplate()?>
         <? endif; ?>
         <? if (!is_array($urls)) $urls = array();
           if(!$this->driver->isCollection()):
@@ -182,7 +183,7 @@
         <? endforeach; ?>
       <? endif; ?>
 
-      <?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?>
+      <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()).'"></span>':''?>
     </div>
   </div>
 </div>
diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml
index b29f944b2667ae6024e385bcfec7c7e1490bcb7e..2965a1b63efe7767aa05120283be7883cec140d2 100644
--- a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml
+++ b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml
@@ -3,11 +3,11 @@
     $account = $this->auth()->getManager();
     $user = $account->isLoggedIn();
     $holdings = $this->driver->getRealTimeHoldings();
-    $openUrl = $this->driver->openURLActive('holdings') ? $this->driver->getOpenURL() : false;
-    $offlineMode = $this->ils()->getOfflineMode();
+    $openUrl = $this->openUrl($this->driver);
+    $openUrlActive = $openUrl->isActive('holdings');
     // Account for replace_other_urls setting
-    $urls = ($openUrl && $this->driver->replaceURLsWithOpenURL()) ? array() : $this->record($this->driver)->getLinkDetails();
-
+    $urls = $openUrlActive ? $this->record($this->driver)->getLinkDetailsForOpenUrl() : $this->record($this->driver)->getLinkDetails() ;
+    $offlineMode = $this->ils()->getOfflineMode();
     // Set page title.
     $this->headTitle($this->translate('Holdings') . ': ' . $this->driver->getBreadcrumb());
 ?>
@@ -39,14 +39,14 @@
 <? $holdingTitleHold = $this->driver->tryMethod('getRealTimeTitleHold'); if (!empty($holdingTitleHold)): ?>
     <a class="placehold modal-link" title="<?=$this->transEsc('request_place_text')?>" href="<?=$this->recordLink()->getRequestUrl($holdingTitleHold)?>"><i class="fa fa-flag"></i>&nbsp;<?=$this->transEsc('title_hold_place')?></a>
 <? endif; ?>
-<? if (!empty($urls) || $openUrl): ?>
+<? if (!empty($urls) || $openUrlActive): ?>
   <h3><?=$this->transEsc("Internet")?></h3>
   <? if (!empty($urls)): ?>
     <? foreach ($urls as $current): ?>
       <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/>
     <? endforeach; ?>
   <? endif; ?>
-  <? if ($openUrl): ?><?=$this->openUrl($openUrl);?><? endif; ?>
+  <? if ($openUrlActive): ?><?=$openUrl->renderTemplate()?><? endif; ?>
 <? endif; ?>
 <? foreach ($holdings as $holding): ?>
 <h3><?=$this->transEsc('location_' . $holding['location'], array(), $holding['location'])?></h3>
diff --git a/themes/bootstrap3/templates/collection/view.phtml b/themes/bootstrap3/templates/collection/view.phtml
index fc4b82ae9b0f092675152b9932ca817432eef854..fc75b7176a7b1e158ae0e95d11420ba036953894 100644
--- a/themes/bootstrap3/templates/collection/view.phtml
+++ b/themes/bootstrap3/templates/collection/view.phtml
@@ -84,7 +84,7 @@
       </div>
     </div>
 
-    <span class="Z3988" title="<?=$this->escapeHtmlAttr($this->driver->getOpenURL())?>"></span>
+    <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenURL()).'"></span>':''?>
   </div>
 
   <? if (isset($activeTabObj) && is_callable(array($activeTabObj, 'getSideRecommendations'))): ?>
diff --git a/themes/bootstrap3/templates/record/view.phtml b/themes/bootstrap3/templates/record/view.phtml
index 5c51ca1750b95953ecf499f29f6afd03987926bd..7ab2b83665832502c20383850af734162bb1937e 100644
--- a/themes/bootstrap3/templates/record/view.phtml
+++ b/themes/bootstrap3/templates/record/view.phtml
@@ -77,7 +77,7 @@
       </div>
     </div>
 
-    <span class="Z3988" title="<?=$this->escapeHtmlAttr($this->driver->getOpenURL())?>"></span>
+    <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="'.$this->escapeHtmlAttr($this->driver->getCoinsOpenURL()).'"></span>':''?>
   </div>
 
   <div class="<?=$this->layoutClass('sidebar')?>">
diff --git a/themes/jquerymobile/templates/RecordDriver/SolrDefault/core.phtml b/themes/jquerymobile/templates/RecordDriver/SolrDefault/core.phtml
index b85be7eae865ab6a5fa2d7748d01f09618a32a4d..5af60c6afa094243fb99d9bb4e68e4d51f1d178d 100644
--- a/themes/jquerymobile/templates/RecordDriver/SolrDefault/core.phtml
+++ b/themes/jquerymobile/templates/RecordDriver/SolrDefault/core.phtml
@@ -133,18 +133,21 @@
   <? endif; ?>
 
   <?
-      $openUrl = $this->driver->openURLActive('record') ? $this->driver->getOpenURL() : false;
+      $openUrl = $this->openUrl($this->driver);
+      $openUrlActive = $openUrl->isActive('record');
       // Account for replace_other_urls setting
-      $urls = ($openUrl && $this->driver->replaceURLsWithOpenURL()) ? array() : $this->record($this->driver)->getLinkDetails();
+       $urls = $openUrlActive
+            ? $this->record($this->driver)->getLinkDetailsForOpenUrl()
+            : $this->record($this->driver)->getLinkDetails();
   ?>
-  <? if (!empty($urls) || $openUrl): ?>
+  <? if (!empty($urls) || $openUrlActive): ?>
     <dt><?=$this->transEsc('Online Access')?>: </dt>
     <dd>
       <? foreach ($urls as $current): ?>
         <p><a rel="external" href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a></p>
       <? endforeach; ?>
-      <? if ($openUrl): ?>
-        <?=$this->openUrl($openUrl)?><br/>
+      <? if ($openUrlActive): ?>
+        <?=$openUrl->renderTemplate()?><br/>
       <? endif; ?>
     </dd>
   <? endif; ?>
diff --git a/themes/jquerymobile/templates/RecordDriver/SolrDefault/result-list.phtml b/themes/jquerymobile/templates/RecordDriver/SolrDefault/result-list.phtml
index 31c5f2e60a9da878d6aed0f728ceb8c04dd69e3e..09b4ccfb8a9319e6a3170ce7f5af86bccc727bbf 100644
--- a/themes/jquerymobile/templates/RecordDriver/SolrDefault/result-list.phtml
+++ b/themes/jquerymobile/templates/RecordDriver/SolrDefault/result-list.phtml
@@ -24,8 +24,11 @@
     <? endif; ?>
     <?=$this->record($this->driver)->getFormatList()?>
     <?
-       $openUrlActive = $this->driver->openURLActive('results');
-       $urls = $this->record($this->driver)->getLinkDetails();
+       $openUrl = $this->openUrl($this->driver);
+       $openUrlActive = $openUrl->isActive('results');
+       $urls = $openUrlActive
+            ? $this->record($this->driver)->getLinkDetailsForOpenUrl()
+            : $this->record($this->driver)->getLinkDetails();
     ?>
     <? if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?>
       <p><span class="ajax_availability hide status"><?=$this->transEsc('Loading')?>...</span></p>