From 0587432f28767a3a48f551898ab61b6bf167e768 Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Fri, 18 Dec 2015 13:41:07 -0500
Subject: [PATCH] Disabled xID services (no longer supported by OCLC). -
 Resolves VUFIND-1142.

---
 config/vufind/WorldCat.ini                    |   1 -
 config/vufind/config.ini                      |  21 +--
 module/VuFind/config/module.config.php        |   6 +-
 module/VuFind/src/VuFind/Config/Upgrade.php   |  40 ++++-
 .../src/VuFind/Connection/WorldCatUtils.php   | 145 ---------------
 .../VuFind/src/VuFind/Related/Deprecated.php  |  54 ++++++
 module/VuFind/src/VuFind/Related/Editions.php | 170 ------------------
 .../src/VuFind/Related/WorldCatEditions.php   | 104 -----------
 .../tests/fixtures/configs/xid/WorldCat.ini   |   3 +
 .../tests/fixtures/configs/xid/config.ini     |  11 ++
 module/VuFind/tests/fixtures/worldcat/xisbn   | Bin 774 -> 0 bytes
 module/VuFind/tests/fixtures/worldcat/xissn   |  18 --
 .../VuFind/tests/fixtures/worldcat/xoclcnum   | Bin 3875 -> 0 bytes
 .../src/VuFindTest/Config/UpgradeTest.php     |  49 ++++-
 .../Connection/WorldCatUtilsTest.php          |  42 -----
 .../templates/Related/Deprecated.phtml        |   2 +
 .../templates/Related/Editions.phtml          |  18 --
 17 files changed, 161 insertions(+), 523 deletions(-)
 create mode 100644 module/VuFind/src/VuFind/Related/Deprecated.php
 delete mode 100644 module/VuFind/src/VuFind/Related/Editions.php
 delete mode 100644 module/VuFind/src/VuFind/Related/WorldCatEditions.php
 create mode 100644 module/VuFind/tests/fixtures/configs/xid/WorldCat.ini
 create mode 100644 module/VuFind/tests/fixtures/configs/xid/config.ini
 delete mode 100644 module/VuFind/tests/fixtures/worldcat/xisbn
 delete mode 100644 module/VuFind/tests/fixtures/worldcat/xissn
 delete mode 100644 module/VuFind/tests/fixtures/worldcat/xoclcnum
 create mode 100644 themes/bootstrap3/templates/Related/Deprecated.phtml
 delete mode 100644 themes/bootstrap3/templates/Related/Editions.phtml

diff --git a/config/vufind/WorldCat.ini b/config/vufind/WorldCat.ini
index 5736b5782a0..f027431e6c3 100644
--- a/config/vufind/WorldCat.ini
+++ b/config/vufind/WorldCat.ini
@@ -54,4 +54,3 @@ Title       = sort_title
 next_prev_navigation = false
 
 related[] = "WorldCatSimilar"
-related[] = "WorldCatEditions"
diff --git a/config/vufind/config.ini b/config/vufind/config.ini
index dc6a0de2f05..406d90ff98f 100644
--- a/config/vufind/config.ini
+++ b/config/vufind/config.ini
@@ -727,25 +727,14 @@ pw               = "Password"
 ;apiId        = myAccessId
 ;apiKey       = mySecretKey
 
-; WorldCat is Optional.  Worldcat offers extra features such as "Other Editions"
-; and the WorldCat searching.
+; This section must be filled in if you plan to use the optional WorldCat
+; search module. Otherwise, it may be ignored.
 ;[WorldCat]
 ;Your WorldCat search API key
-;apiKey          = ApiKey
+;apiKey          = "long-search-api-key-goes-here"
 ;Your holdings symbol (usually a three-letter code) - used for excluding your
 ; institution's holdings from the search results.
 ;OCLCCode        = MYCODE
-; Your worldcat xID Affiliate ID - used for Other Editions feature
-;id              = myAccount
-; If you have increased your OCLC xID daily limits using the token method,
-; fill in those credentials here.
-; note: xOCLCNUM uses the xISBN token/secret
-;xISBN_token = myToken
-;xISBN_secret = mySecret
-; As of 2014-09, although not specified by OCLC's documentation, the xISSN call
-; does not work if you have not also specified your affiliate ID.
-;xISSN_token = myToken
-;xISSN_secret = mySecret
 
 ; DPLA key -- uncomment and fill in to use DPLATerms recommendations (see also
 ; searches.ini).
@@ -1119,13 +1108,9 @@ hide_holdings[] = "World Wide Web"
 ; record view page.
 ;
 ; Available options:
-;    Editions - Alternate editions (in the Solr index) based on WorldCat lookup
 ;    Similar - Similarity based on Solr lookup
-;    WorldCatEditions - Alternate editions (in the WorldCat index) based on WorldCat
-;        lookup
 ;    WorldCatSimilar - Similarity based on WorldCat lookup
 related[] = "Similar"
-related[] = "Editions"
 
 ; This setting controls which citations are available; set to true for all supported
 ; options (default); set to false to disable citations; set to a comma-separated list
diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index 247c4dfbfe8..9081341123c 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -494,11 +494,13 @@ $config = [
             'related' => [
                 'abstract_factories' => ['VuFind\Related\PluginFactory'],
                 'factories' => [
-                    'editions' => 'VuFind\Related\Factory::getEditions',
                     'similar' => 'VuFind\Related\Factory::getSimilar',
-                    'worldcateditions' => 'VuFind\Related\Factory::getWorldCatEditions',
                     'worldcatsimilar' => 'VuFind\Related\Factory::getWorldCatSimilar',
                 ],
+                'invokables' => [
+                    'editions' => 'VuFind\Related\Deprecated',
+                    'worldcateditions' => 'VuFind\Related\Deprecated',
+                ],
             ],
             'resolver_driver' => [
                 'abstract_factories' => ['VuFind\Resolver\Driver\PluginFactory'],
diff --git a/module/VuFind/src/VuFind/Config/Upgrade.php b/module/VuFind/src/VuFind/Config/Upgrade.php
index 4b0d9189e03..e778de4cdcd 100644
--- a/module/VuFind/src/VuFind/Config/Upgrade.php
+++ b/module/VuFind/src/VuFind/Config/Upgrade.php
@@ -565,7 +565,7 @@ class Upgrade
             }
         }
 
-        // Warn the user about deprecated WorldCat setting:
+        // Warn the user about deprecated WorldCat settings:
         if (isset($newConfig['WorldCat']['LimitCodes'])) {
             unset($newConfig['WorldCat']['LimitCodes']);
             $this->addWarning(
@@ -573,6 +573,29 @@ class Upgrade
                 . ' removed.'
             );
         }
+        $badKeys
+            = ['id', 'xISBN_token', 'xISBN_secret', 'xISSN_token', 'xISSN_secret'];
+        foreach ($badKeys as $key) {
+            if (isset($newConfig['WorldCat'][$key])) {
+                unset($newConfig['WorldCat'][$key]);
+                $this->addWarning(
+                    'The [WorldCat] ' . $key . ' setting is no longer used and'
+                    . ' has been removed.'
+                );
+            }
+        }
+        if (isset($newConfig['Record']['related'])
+            && in_array('Editions', $newConfig['Record']['related'])
+        ) {
+            $newConfig['Record']['related'] = array_diff(
+                $newConfig['Record']['related'], ['Editions']
+            );
+            $this->addWarning(
+                'The Editions related record module is no longer '
+                . 'supported due to OCLC\'s xID API shutdown.'
+                . ' It has been removed from your settings.'
+            );
+        }
 
         // Upgrade Google Options:
         if (isset($newConfig['Content']['GoogleOptions'])
@@ -1081,6 +1104,21 @@ class Upgrade
             $this->newConfigs['WorldCat.ini'][$section] = $new;
         }
 
+        // Deal with deprecated related record module.
+        $newConfig = & $this->newConfigs['WorldCat.ini'];
+        if (isset($newConfig['Record']['related'])
+            && in_array('WorldCatEditions', $newConfig['Record']['related'])
+        ) {
+            $newConfig['Record']['related'] = array_diff(
+                $newConfig['Record']['related'], ['WorldCatEditions']
+            );
+            $this->addWarning(
+                'The WorldCatEditions related record module is no longer '
+                . 'supported due to OCLC\'s xID API shutdown.'
+                . ' It has been removed from your settings.'
+            );
+        }
+
         // save the file
         $this->saveModifiedConfig('WorldCat.ini');
     }
diff --git a/module/VuFind/src/VuFind/Connection/WorldCatUtils.php b/module/VuFind/src/VuFind/Connection/WorldCatUtils.php
index dfec2f27ca5..6b08b282242 100644
--- a/module/VuFind/src/VuFind/Connection/WorldCatUtils.php
+++ b/module/VuFind/src/VuFind/Connection/WorldCatUtils.php
@@ -118,151 +118,6 @@ class WorldCatUtils implements \Zend\Log\LoggerAwareInterface
         return null;
     }
 
-    /**
-     * Get the WorldCat ID from the config file.
-     *
-     * @return string
-     */
-    protected function getWorldCatId()
-    {
-        return isset($this->config->id) ? $this->config->id : false;
-    }
-
-    /**
-     * Build a url to use in querying OCLC's xID service.
-     *
-     * @param string $base      base url with no querystring
-     * @param string $tokenVar  config file variable holding the token
-     * @param string $secretVar config file variable holding the secret
-     * @param string $format    data format for api response
-     *
-     * @return string
-     */
-    protected function buildXIdUrl($base, $tokenVar, $secretVar, $format)
-    {
-        $token = isset($this->config->$tokenVar)
-            ? $this->config->$tokenVar : false;
-        $secret = isset($this->config->$secretVar)
-            ? $this->config->$secretVar : false;
-        $querystr = '?method=getEditions&format=' . $format;
-        if ($token && $secret) {
-            $hash = md5($base . '|' . $this->ip . '|' . $secret);
-            $querystr .= '&token=' . $token . '&hash=' . $hash;
-        } if ($wcId = $this->getWorldCatId()) {
-            $querystr .= '&ai=' . urlencode($wcId);
-        }
-        $base .= $querystr;
-        return $base;
-    }
-
-    /**
-     * Retrieve results from the index using the XISBN service.
-     *
-     * @param string $isbn ISBN of main record
-     *
-     * @return array       ISBNs for related items (may be empty).
-     */
-    public function getXISBN($isbn)
-    {
-        // Build URL
-        $base = 'http://xisbn.worldcat.org/webservices/xid/isbn/' .
-                urlencode(is_array($isbn) ? $isbn[0] : $isbn);
-        $url = $this->buildXIdUrl($base, 'xISBN_token', 'xISBN_secret', 'json');
-
-        // Print Debug code
-        $this->debug("XISBN: $url");
-        $response = json_decode($this->retrieve($url));
-
-        // Fetch results
-        $isbns = [];
-        if (isset($response->list)) {
-            foreach ($response->list as $line) {
-                // Filter out non-ISBN characters and validate the length of
-                // whatever is left behind; this will prevent us from treating
-                // error messages like "invalidId" or "overlimit" as ISBNs.
-                $isbn = preg_replace(
-                    '/[^0-9xX]/', '', isset($line->isbn[0]) ? $line->isbn[0] : ''
-                );
-                if (strlen($isbn) >= 10) {
-                    $isbns[] = $isbn;
-                }
-            }
-        }
-        return $isbns;
-    }
-
-    /**
-     * Retrieve results from the index using the XOCLCNUM service.
-     *
-     * @param string $oclc OCLC number of main record
-     *
-     * @return array       ISBNs for related items (may be empty).
-     */
-    public function getXOCLCNUM($oclc)
-    {
-        // Build URL
-        $base = 'http://xisbn.worldcat.org/webservices/xid/oclcnum/' .
-                urlencode(is_array($oclc) ? $oclc[0] : $oclc);
-        $url = $this->buildXIdUrl($base, 'xISBN_token', 'xISBN_secret', 'json');
-
-        // Print Debug code
-        $this->debug("XOCLCNUM: $url");
-        $response = json_decode($this->retrieve($url));
-
-        // Fetch results
-        $results = [];
-        if (isset($response->list)) {
-            foreach ($response->list as $line) {
-                $values = isset($line->oclcnum) ? $line->oclcnum : [];
-                foreach ($values as $data) {
-                    // Filter out non-numeric characters and validate the length of
-                    // whatever is left behind; this will prevent us from treating
-                    // error messages like "invalidId" or "overlimit" as ISBNs.
-                    $current = preg_replace('/[^0-9]/', '', $data);
-                    if (!empty($current)) {
-                        $results[] = $current;
-                    }
-                }
-            }
-        }
-
-        return array_unique($results);
-    }
-
-    /**
-     * Retrieve results from the index using the XISSN service.
-     *
-     * @param string $issn ISSN of main record
-     *
-     * @return array       ISSNs for related items (may be empty).
-     */
-    public function getXISSN($issn)
-    {
-        // Build URL
-        $base = 'http://xissn.worldcat.org/webservices/xid/issn/' .
-                urlencode(is_array($issn) ? $issn[0] : $issn);
-        $url = $this->buildXIdUrl($base, 'xISSN_token', 'xISSN_secret', 'xml');
-
-        // Print Debug code
-        $this->debug("XISSN: $url");
-
-        // Fetch results
-        $issns = [];
-        $xml = $this->retrieve($url);
-        if (!empty($xml)) {
-            $data = simplexml_load_string($xml);
-            if (!empty($data) && isset($data->group->issn)
-                && count($data->group->issn) > 0
-            ) {
-                foreach ($data->group->issn as $issn) {
-                    $issns[] = (string)$issn;
-                }
-            }
-        }
-
-        return $issns;
-    }
-
     /**
      * Support function for getIdentitiesQuery(); is the provided name component
      * worth considering as a first or last name?
diff --git a/module/VuFind/src/VuFind/Related/Deprecated.php b/module/VuFind/src/VuFind/Related/Deprecated.php
new file mode 100644
index 00000000000..4b630c34eed
--- /dev/null
+++ b/module/VuFind/src/VuFind/Related/Deprecated.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Deprecated Related Records Module - used to replace legacy modules that no
+ * longer function due to, for example, external APIs that have been shut down.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 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  Related_Records
+ * @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:building_a_related_record_module Wiki
+ */
+namespace VuFind\Related;
+
+/**
+ * Deprecated Related Records Module - used to replace legacy modules that no
+ * longer function due to, for example, external APIs that have been shut down.
+ *
+ * @category VuFind2
+ * @package  Related_Records
+ * @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:building_a_related_record_module Wiki
+ */
+class Deprecated implements RelatedInterface
+{
+    /**
+     * Establishes base settings for making recommendations.
+     *
+     * @param string                            $settings Settings from config.ini
+     * @param \VuFind\RecordDriver\AbstractBase $driver   Record driver object
+     *
+     * @return void
+     */
+    public function init($settings, $driver)
+    {
+    }
+}
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Related/Editions.php b/module/VuFind/src/VuFind/Related/Editions.php
deleted file mode 100644
index eeeaf989d1c..00000000000
--- a/module/VuFind/src/VuFind/Related/Editions.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-/**
- * Related Records: WorldCat-based editions list (Solr results)
- *
- * PHP version 5
- *
- * Copyright (C) Villanova University 2009.
- *
- * 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  Related_Records
- * @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:building_a_related_record_module Wiki
- */
-namespace VuFind\Related;
-
-/**
- * Related Records: WorldCat-based editions list (Solr results)
- *
- * @category VuFind2
- * @package  Related_Records
- * @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:building_a_related_record_module Wiki
- */
-class Editions implements RelatedInterface
-{
-    /**
-     * Related editions
-     *
-     * @var array
-     */
-    protected $results = [];
-
-    /**
-     * Results plugin manager
-     *
-     * @var \VuFind\Search\Results\PluginManager
-     */
-    protected $resultsManager;
-
-    /**
-     * WorldCat utilities
-     *
-     * @var \VuFind\Connection\WorldCatUtils
-     */
-    protected $wcUtils;
-
-    /**
-     * Maximum limit
-     *
-     * @var int
-     */
-    protected $maxLimit;
-
-    /**
-     * Constructor
-     *
-     * @param \VuFind\Search\Results\PluginManager $results  Results plugin manager
-     * @param \VuFind\Connection\WorldCatUtils     $wcUtils  WorldCat utils object
-     * @param int                                  $maxLimit The maximum number of
-     * results to look up in Solr; set to -1 to use the "max boolean clauses" limit.
-     */
-    public function __construct(\VuFind\Search\Results\PluginManager $results,
-        \VuFind\Connection\WorldCatUtils $wcUtils, $maxLimit = -1
-    ) {
-        $this->resultsManager = $results;
-        $this->wcUtils = $wcUtils;
-        $this->maxLimit = $maxLimit;
-    }
-
-    /**
-     * Establishes base settings for making recommendations.
-     *
-     * @param string                            $settings Settings from config.ini
-     * @param \VuFind\RecordDriver\AbstractBase $driver   Record driver object
-     *
-     * @return void
-     */
-    public function init($settings, $driver)
-    {
-        // If we have query parts, we should try to find related records:
-        $parts = $this->getQueryParts($driver);
-        if (!empty($parts)) {
-            // Limit the number of parts based on the boolean clause limit:
-            $result = $this->resultsManager->get('Solr');
-            $params = $result->getParams();
-            $params->getOptions()->spellcheckEnabled(false);
-            $limit = $params->getQueryIDLimit();
-            if ($this->maxLimit > 0 && $limit > $this->maxLimit) {
-                $limit = $this->maxLimit;
-            }
-            if (count($parts) > $limit) {
-                $parts = array_slice($parts, 0, $limit);
-            }
-
-            // Assemble the query parts and filter out current record if it comes
-            // from the Solr index.:
-            $query = '(' . implode(' OR ', $parts) . ')';
-            if ($driver->getResourceSource() == 'VuFind') {
-                $query .= ' NOT id:"' . addcslashes($driver->getUniqueID(), '"')
-                    . '"';
-            }
-
-            // Perform the search and return either results or an error:
-            $params->setLimit(5);
-            $params->setOverrideQuery($query);
-            $this->results = $result->getResults();
-        }
-    }
-
-    /**
-     * Get an array of Record Driver objects representing other editions of the one
-     * passed to the constructor.
-     *
-     * @return array
-     */
-    public function getResults()
-    {
-        return $this->results;
-    }
-
-    /**
-     * Try to build an array of OCLC Number, ISBN or ISSN-based sub-queries by
-     * using OCLC X-services against a record driver object.
-     *
-     * @param \VuFind\RecordDriver\AbstractBase $driver Record driver object
-     *
-     * @return array
-     */
-    protected function getQueryParts($driver)
-    {
-        $parts = [];
-        $oclcNum = $driver->tryMethod('getCleanOCLCNum');
-        if (!empty($oclcNum)) {
-            $oclcList = $this->wcUtils->getXOCLCNUM($oclcNum);
-            foreach ($oclcList as $current) {
-                $parts[] = "oclc_num:" . $current;
-            }
-        }
-        $isbn = $driver->tryMethod('getCleanISBN');
-        if (!empty($isbn)) {
-            $isbnList = $this->wcUtils->getXISBN($isbn);
-            foreach ($isbnList as $current) {
-                $parts[] = 'isbn:' . $current;
-            }
-        }
-        $issn = $driver->tryMethod('getCleanISSN');
-        if (!empty($issn)) {
-            $issnList = $this->wcUtils->getXISSN($issn);
-            foreach ($issnList as $current) {
-                $parts[] = 'issn:' . $current;
-            }
-        }
-        return $parts;
-    }
-}
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Related/WorldCatEditions.php b/module/VuFind/src/VuFind/Related/WorldCatEditions.php
deleted file mode 100644
index fa2e864a211..00000000000
--- a/module/VuFind/src/VuFind/Related/WorldCatEditions.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-/**
- * Related Records: WorldCat-based editions list (WorldCat results)
- *
- * PHP version 5
- *
- * Copyright (C) Villanova University 2009.
- *
- * 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  Related_Records
- * @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:building_a_related_record_module Wiki
- */
-namespace VuFind\Related;
-
-/**
- * Related Records: WorldCat-based editions list (WorldCat results)
- *
- * @category VuFind2
- * @package  Related_Records
- * @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:building_a_related_record_module Wiki
- */
-class WorldCatEditions extends Editions
-{
-    /**
-     * Establishes base settings for making recommendations.
-     *
-     * @param string                            $settings Settings from config.ini
-     * @param \VuFind\RecordDriver\AbstractBase $driver   Record driver object
-     *
-     * @return void
-     */
-    public function init($settings, $driver)
-    {
-        // If we have query parts, we should try to find related records:
-        $parts = $this->getQueryParts($driver);
-        if (!empty($parts)) {
-            // Assemble the query parts and filter out current record if it comes
-            // from the Solr index.:
-            $query = '(' . implode(' or ', $parts) . ')';
-            if ($driver->getResourceSource() == 'WorldCat') {
-                $query .= ' not srw.no all ' . $driver->getUniqueID();
-            }
-
-            // Perform the search and save results:
-            $result = $this->resultsManager->get('WorldCat');
-            $params = $result->getParams();
-            $params->setLimit(5);
-            $params->setOverrideQuery($query);
-            $this->results = $result->getResults();
-        }
-    }
-
-    /**
-     * Try to build an array of OCLC Number, ISBN or ISSN-based sub-queries by
-     * using OCLC X-services against a record driver object.
-     *
-     * @param \VuFind\RecordDriver\AbstractBase $driver Record driver object
-     *
-     * @return array
-     */
-    protected function getQueryParts($driver)
-    {
-        $parts = [];
-        $oclcNum = $driver->tryMethod('getCleanOCLCNum');
-        if (!empty($oclcNum)) {
-            $oclcList = $this->wcUtils->getXOCLCNUM($oclcNum);
-            if (!empty($oclcList)) {
-                $parts[] = '(srw.no any "' . implode(' ', $oclcList) . '")';
-            }
-        }
-        $isbn = $driver->tryMethod('getCleanISBN');
-        if (!empty($isbn)) {
-            $isbnList = $this->wcUtils->getXISBN($isbn);
-            if (!empty($isbnList)) {
-                $parts[] = '(srw.bn any "' . implode(' ', $isbnList) . '")';
-            }
-        }
-        $issn = $driver->tryMethod('getCleanISSN');
-        if (!empty($issn)) {
-            $issnList = $this->wcUtils->getXISSN($issn);
-            if (!empty($issnList)) {
-                $parts[] = '(srw.sn any "' . implode(' ', $issnList) . '")';
-            }
-        }
-        return $parts;
-    }
-}
diff --git a/module/VuFind/tests/fixtures/configs/xid/WorldCat.ini b/module/VuFind/tests/fixtures/configs/xid/WorldCat.ini
new file mode 100644
index 00000000000..7757a570197
--- /dev/null
+++ b/module/VuFind/tests/fixtures/configs/xid/WorldCat.ini
@@ -0,0 +1,3 @@
+[Record]
+related[] = "WorldCatSimilar"
+related[] = "WorldCatEditions"
\ No newline at end of file
diff --git a/module/VuFind/tests/fixtures/configs/xid/config.ini b/module/VuFind/tests/fixtures/configs/xid/config.ini
new file mode 100644
index 00000000000..f27b6d6e2c4
--- /dev/null
+++ b/module/VuFind/tests/fixtures/configs/xid/config.ini
@@ -0,0 +1,11 @@
+[Record]
+related[] = "Similar"
+related[] = "Editions"
+
+[WorldCat]
+apiKey = foo
+id = bar
+xISBN_token = myToken1
+xISBN_secret = mySecret1
+xISSN_token = myToken2
+xISSN_secret = mySecret2
diff --git a/module/VuFind/tests/fixtures/worldcat/xisbn b/module/VuFind/tests/fixtures/worldcat/xisbn
deleted file mode 100644
index d57a21bb8d5e9a3270729b5cbff1892f993bcd6c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 774
zcmV+h1Nr<&R8&weF)lG6GB7Y8PfHC7Q)O~?WpX+oL2zMXXk{%!Z+UNYWiK%<F%1es
zZ*FvDZgeeFd2nSqAarGTbT4phVQFqVV`yP=b7gcrRa8bTI1LIzZ*FvDZgee0Zewp`
zX>MmaAZL1Ma19DpVRCsoAVFheWpH#YMQ&qnWNB_^4GKhIbY(gqMsjH^ATcr^Q)O@<
zGB7bTATc&NH8DCdFd#=wR1FG4Z*Fd7V{~b6ZaN@iY;SXA4GIkkABzY800000004bf
zyKYob5Ua^opsgw9{oqTGPzvZG5EMxl<=^pP1rm}k&1$c_bMB0f?b+9d_qXSlFE6)`
zx1Zl`AMo`3=^0O-UmxDxo}Rz{z}K5-NO-FG?ePu2dH)YMvbmkXKopiqc5*P>R9ox<
z4E3#HV0J~d7CkmsOw{>1bGOR9i;ZF~tve$O)V0yxSPF)US~q2=P<4)`mD#T9n22>-
z*6TjO8UiRb7Ln}OR!MD;M8T5oHmBW22}bexbOnN@3C>WUolaT_h87JQ2T$cpapF<g
z&;q^B$@IuFMgqXr#Fg{PXq{PM3_(02DSXa5h!3VA=ypU$oXx3WEzu%WeZ33jg*F5U
zn1CZ?4S^tqmAa`+LJe~owZlLKnrU@8XH^dM=d@4mGzC{`G;)~Y{}8ViSC*oh7Yt1y
zg_>UWxe-&DqLmn8Y<ZeMU<Ofl(qj*~yF-Z=nA%q1VCqjvlU-vZfS`Z}<s$p0@YY_M
z34u9Qc=p{%uu4k8xl4??7x8v1=-1ijWqx7%oN~EvHiWxv)GMf5Xv~bhvpxK<Pv`6o
zakrrFbTm_#hUP62Y#JVA#G}#$HZyvklMpUKlXW|AQXV0(${pVZLqgSu0~4W2?I3>_
zC<7s`go>z4?mHWsg%cmhph&t`K>S9Y{XjJ|sz=v!6r^o=p#DF0pzG>nv<w*Hm1ogG
zEc8~+-Zd>M>gnG*yM}dpkGI7JrT=Qw^4Nv0j<tpugf}#BQ6t_CeUERy0IO7R{9y?I
E0F~-e4FCWD

diff --git a/module/VuFind/tests/fixtures/worldcat/xissn b/module/VuFind/tests/fixtures/worldcat/xissn
deleted file mode 100644
index 6d6fb1e36ab..00000000000
--- a/module/VuFind/tests/fixtures/worldcat/xissn
+++ /dev/null
@@ -1,18 +0,0 @@
-HTTP/1.1 200 OK
-Server: Apache-Coyote/1.1
-Content-Type: text/xml;charset=UTF-8
-Content-Length: 1817
-Date: Fri, 12 Sep 2014 17:17:29 GMT
-Connection: close
-
-<?xml version="1.0" encoding="UTF-8"?> 
-<rsp xmlns="http://worldcat.org/xid/issn/" stat="ok">
-      <group  rel="this"  >
-      <issn  form="JC"   oclcnum="27999397 614775952 828258510"   title="The New York times" publisher="Ann Arbor, MI : University Microfilms"            issnl="0362-4331" >1542-667X</issn>
-<issn  form="JD"   oclcnum="848189732 869683812 34107580 819006198 475400139"   rawcoverage="Print began with vol. 6, no. 1868 (Sept. 14, 1857)." title="The New York times on the Web" publisher="New York, N.Y. : New York Times"            issnl="0362-4331" >1553-8095</issn>
-<issn  form="JB"   oclcnum="185495447 173336391 798566583 855519714 862776474 436637822 799670030 875826368 715521852 611398715 721108740 477255725 871183240 757341285 1760220 433669505 783915798 783922118 760532692 793841904 719164751 474217043 809570612 184852924 423444534 760046843 863161983 2414466 802634886"   rawcoverage="v. 1-   Jan./Mar. 1913-" title="The New York times index" publisher="New York, New York Times Co"            issnl="0147-538X" >0147-538X</issn>
-<issn  form="JB"   oclcnum="858481778 289500397 858131820 772652747 798923343 423444554 315567303 812136736 612271274 473828002 503228989 872186574 427348605 850514978 762018412 367767356 436627352 757371407 174295982 433738063 638670150 137342435 62313540 652207512 13446366 22436204 25127357 746941922 5657581 612958371 875973682 801785886 1645522 647946096 243424919 773494446 486812086 848537359 472456118 613508528 530930573 719926643 823513770 641015517 802636502 811616918 173858512 474808737 643839034 219854517 42957207 433689829 637645077 440842696"   rawcoverage="Vol. 6, no. 1868 (Sept. 14, 1857)-" title="The New York times" publisher="New-York N.Y. : H.J. Raymond &amp; Co"            issnl="0362-4331" >0362-4331</issn>
- 
-</group>
- 
-</rsp>
diff --git a/module/VuFind/tests/fixtures/worldcat/xoclcnum b/module/VuFind/tests/fixtures/worldcat/xoclcnum
deleted file mode 100644
index db2f0c1b14f61bf4815a06fe0c70243bed581f98..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 3875
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

diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/UpgradeTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/UpgradeTest.php
index f40bfeb617a..71074a340da 100644
--- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/UpgradeTest.php
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/UpgradeTest.php
@@ -90,22 +90,34 @@ class UpgradeTest extends \VuFindTest\Unit\TestCase
         // Prior to 2.4, we expect exactly one warning about using a deprecated
         // theme:
         if ((float)$version < 1.3) {
-            $this->assertEquals(1, count($warnings));
+            $this->assertEquals(2, count($warnings));
+            $this->assertEquals(
+                'The Editions related record module is no longer '
+                . 'supported due to OCLC\'s xID API shutdown.'
+                . ' It has been removed from your settings.',
+                $warnings[0]
+            );
             $this->assertEquals(
                 "WARNING: This version of VuFind does not support "
                 . "the default theme.  Your config.ini [Site] theme setting "
                 . "has been reset to the default: bootprint3.  You may need to "
                 . "reimplement your custom theme.",
-                $warnings[0]
+                $warnings[1]
             );
         } else if ((float)$version < 2.4) {
-            $this->assertEquals(1, count($warnings));
+            $this->assertEquals(2, count($warnings));
+            $this->assertEquals(
+                'The Editions related record module is no longer '
+                . 'supported due to OCLC\'s xID API shutdown.'
+                . ' It has been removed from your settings.',
+                $warnings[0]
+            );
             $this->assertEquals(
                 "WARNING: This version of VuFind does not support "
                 . "the blueprint theme.  Your config.ini [Site] theme setting "
                 . "has been reset to the default: bootprint3.  You may need to "
                 . "reimplement your custom theme.",
-                $warnings[0]
+                $warnings[1]
             );
         } else {
             $this->assertEquals(0, count($warnings));
@@ -269,6 +281,35 @@ class UpgradeTest extends \VuFindTest\Unit\TestCase
         );
     }
 
+    /**
+     * Test removal of xID settings
+     *
+     * @return void
+     */
+    public function testXidDeprecation()
+    {
+        $upgrader = $this->getUpgrader('xid');
+        $upgrader->run();
+        $results = $upgrader->getNewConfigs();
+        $this->assertEquals(
+            ['Similar'], $results['config.ini']['Record']['related']
+        );
+        $this->assertEquals(
+            ['WorldCatSimilar'], $results['WorldCat.ini']['Record']['related']
+        );
+        $this->assertEquals(['apiKey' => 'foo'], $results['config.ini']['WorldCat']);
+        $expectedWarnings = [
+            'The [WorldCat] id setting is no longer used and has been removed.',
+            'The [WorldCat] xISBN_token setting is no longer used and has been removed.',
+            'The [WorldCat] xISBN_secret setting is no longer used and has been removed.',
+            'The [WorldCat] xISSN_token setting is no longer used and has been removed.',
+            'The [WorldCat] xISSN_secret setting is no longer used and has been removed.',
+            'The Editions related record module is no longer supported due to OCLC\'s xID API shutdown. It has been removed from your settings.',
+            'The WorldCatEditions related record module is no longer supported due to OCLC\'s xID API shutdown. It has been removed from your settings.',
+        ];
+        $this->assertEquals($expectedWarnings, $upgrader->getWarnings());
+    }
+
     /**
      * Test permission upgrade
      *
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WorldCatUtilsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WorldCatUtilsTest.php
index 42979533887..89d4a511050 100644
--- a/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WorldCatUtilsTest.php
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Connection/WorldCatUtilsTest.php
@@ -44,48 +44,6 @@ use Zend\Http\Client as HttpClient;
  */
 class WorldCatUtilsTest extends \PHPUnit_Framework_TestCase
 {
-    /**
-     * Test XISBN processing.
-     *
-     * @return void
-     */
-    public function testXISBN()
-    {
-        $client = $this->getClient('xisbn');
-        $result = $client->getXISBN('0140435123');
-        $this->assertEquals(82, count($result));
-        $this->assertTrue(in_array('0140435123', $result));
-        $this->assertTrue(in_array('1848378912', $result));
-    }
-
-    /**
-     * Test XISSN processing.
-     *
-     * @return void
-     */
-    public function testXISSN()
-    {
-        $client = $this->getClient('xissn');
-        $result = $client->getXISSN('0362-4331');
-        $this->assertEquals(4, count($result));
-        $this->assertTrue(in_array('0362-4331', $result));
-        $this->assertTrue(in_array('1542-667X', $result));
-    }
-
-    /**
-     * Test XOCLCNUM processing.
-     *
-     * @return void
-     */
-    public function testXOCLCNUM()
-    {
-        $client = $this->getClient('xoclcnum');
-        $result = $client->getXOCLCNUM('42371494');
-        $this->assertEquals(568, count($result));
-        $this->assertTrue(in_array('42371494', $result));
-        $this->assertTrue(in_array('1710732', $result));
-    }
-
     /**
      * Test related identities
      *
diff --git a/themes/bootstrap3/templates/Related/Deprecated.phtml b/themes/bootstrap3/templates/Related/Deprecated.phtml
new file mode 100644
index 00000000000..63445d34f9c
--- /dev/null
+++ b/themes/bootstrap3/templates/Related/Deprecated.phtml
@@ -0,0 +1,2 @@
+<? /* do nothing -- this module is a placeholder for old deprecated features
+      to prevent legacy configurations from causing fatal errors. */ ?>
\ No newline at end of file
diff --git a/themes/bootstrap3/templates/Related/Editions.phtml b/themes/bootstrap3/templates/Related/Editions.phtml
deleted file mode 100644
index c04b4719009..00000000000
--- a/themes/bootstrap3/templates/Related/Editions.phtml
+++ /dev/null
@@ -1,18 +0,0 @@
-<? $editions = $this->related->getResults(); if (!empty($editions)): ?>
-  <h4><?=$this->transEsc('Other Editions')?></h4>
-  <ul class="list-group">
-    <? foreach ($editions as $data): ?>
-      <li class="list-group-item">
-        <? $formats = $data->getFormats(); ?>
-        <i class="fa fa-x<? if (count($formats) > 0): ?> fa-<?=preg_replace('/[^a-z0-9]/', '', strtolower($formats[0]))?>" title="<?=$formats[0] ?><? endif; ?>"></i>
-        <a href="<?=$this->recordLink()->getUrl($data)?>"><?=$this->escapeHtml($data->getTitle())?></a>
-        <? $author = $data->getPrimaryAuthor(); if (!empty($author)): ?>
-          <br/><?=$this->transEsc('By')?>: <?=$this->escapeHtml($author);?>
-        <? endif; ?>
-        <? $pubDates = $data->getPublicationDates(); if (!empty($pubDates)): ?>
-          <?=$this->transEsc('Published')?>: (<?=$this->escapeHtml($pubDates[0])?>)
-        <? endif; ?>
-      </li>
-    <? endforeach; ?>
-  </ul>
-<? endif; ?>
\ No newline at end of file
-- 
GitLab