From 32d4cea8628c62b7c06b0bc23274e92271a8ce3e Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Mon, 8 Jul 2019 12:37:30 -0400
Subject: [PATCH] New recommendation module: link to external search (#1385)

---
 config/vufind/searches.ini                    |   7 +
 .../src/VuFind/Recommend/ExternalSearch.php   | 129 ++++++++++++++++++
 .../src/VuFind/Recommend/PluginManager.php    |   2 +
 .../Recommend/ExternalSearchTest.php          |  99 ++++++++++++++
 .../templates/Recommend/ExternalSearch.phtml  |   3 +
 5 files changed, 240 insertions(+)
 create mode 100644 module/VuFind/src/VuFind/Recommend/ExternalSearch.php
 create mode 100644 module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExternalSearchTest.php
 create mode 100644 themes/bootstrap3/templates/Recommend/ExternalSearch.phtml

diff --git a/config/vufind/searches.ini b/config/vufind/searches.ini
index 45ac7dda100..33888052f68 100644
--- a/config/vufind/searches.ini
+++ b/config/vufind/searches.ini
@@ -375,6 +375,13 @@ CallNumber = callnumber-sort
 ;       display a link to resolve it. If it makes up the entire query, redirect to
 ;       the resolver automatically. [prefix] is the URL of the resolver to which
 ;       the DOI itself will be appended.
+; ExternalSearch:[link label]:[url template]
+;       Display a link to an external search system. The contents of the <a> tag
+;       will be [link label] (which will be run through the translator -- use a
+;       translation string if you need to display a string containing a colon).
+;       The href will be [url template] with %%lookfor%% replaced with the current
+;       search's lookfor parameter. If you omit the %%lookfor%% placeholder, the
+;       URL will simply be appended to the end of the string.
 ; Libraryh3lp:[type]:[id]:[skin]
 ;       Display a chat box for the Libraryh3lp service. [type] indicats the type
 ;       of chat being used (either "queue" or "user"). [id] is the name of the
diff --git a/module/VuFind/src/VuFind/Recommend/ExternalSearch.php b/module/VuFind/src/VuFind/Recommend/ExternalSearch.php
new file mode 100644
index 00000000000..04233927846
--- /dev/null
+++ b/module/VuFind/src/VuFind/Recommend/ExternalSearch.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * ExternalSearch Recommendation Module
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2019.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  Recommendations
+ * @author   Mark Triggs <vufind-tech@lists.sourceforge.net>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Page
+ */
+namespace VuFind\Recommend;
+
+/**
+ * ExternalSearch Recommendation Module
+ *
+ * @category VuFind
+ * @package  Recommendations
+ * @author   Chris Hallberg <challber@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Page
+ */
+class ExternalSearch implements RecommendInterface
+{
+    /**
+     * Link text
+     *
+     * @var string
+     */
+    protected $linkText;
+
+    /**
+     * URL template string
+     *
+     * @var string
+     */
+    protected $template;
+
+    /**
+     * Search query
+     *
+     * @var string
+     */
+    protected $lookfor;
+
+    /**
+     * Store the configuration of the recommendation module.
+     *
+     * @param string $settingsStr Settings from searches.ini.
+     *
+     * @return void
+     */
+    public function setConfig($settingsStr)
+    {
+        // Parse the settings:
+        $settings = explode(':', $settingsStr, 2);
+        $this->linkText = $settings[0] ?? null;
+        $this->template = $settings[1] ?? null;
+    }
+
+    /**
+     * Called at the end of the Search Params objects' initFromRequest() method.
+     * This method is responsible for setting search parameters needed by the
+     * recommendation module and for reading any existing search parameters that may
+     * be needed.
+     *
+     * @param \VuFind\Search\Base\Params $params  Search parameter object
+     * @param \Zend\StdLib\Parameters    $request Parameter object representing user
+     * request.
+     *
+     * @return void
+     */
+    public function init($params, $request)
+    {
+        $this->lookfor = $request->get('lookfor');
+    }
+
+    /**
+     * Called after the Search Results object has performed its main search.  This
+     * may be used to extract necessary information from the Search Results object
+     * or to perform completely unrelated processing.
+     *
+     * @param \VuFind\Search\Base\Results $results Search results object
+     *
+     * @return void
+     */
+    public function process($results)
+    {
+        // no action needed
+    }
+
+    /**
+     * Get the link text.
+     *
+     * @return string
+     */
+    public function getLinkText()
+    {
+        return $this->linkText;
+    }
+
+    /**
+     * Get the link URL.
+     *
+     * @return string
+     */
+    public function getUrl()
+    {
+        return (false === strpos($this->template, '%%lookfor%%'))
+            ? $this->template . urlencode($this->lookfor)
+            : str_replace('%%lookfor%%', urlencode($this->lookfor), $this->template);
+    }
+}
diff --git a/module/VuFind/src/VuFind/Recommend/PluginManager.php b/module/VuFind/src/VuFind/Recommend/PluginManager.php
index e18a7bc0308..181447c81b3 100644
--- a/module/VuFind/src/VuFind/Recommend/PluginManager.php
+++ b/module/VuFind/src/VuFind/Recommend/PluginManager.php
@@ -58,6 +58,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         'europeanaresults' => EuropeanaResults::class,
         'europeanaresultsdeferred' => EuropeanaResultsDeferred::class,
         'expandfacets' => ExpandFacets::class,
+        'externalsearch' => ExternalSearch::class,
         'facetcloud' => FacetCloud::class,
         'favoritefacets' => FavoriteFacets::class,
         'libraryh3lp' => Libraryh3lp::class,
@@ -106,6 +107,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         EuropeanaResults::class => EuropeanaResultsFactory::class,
         EuropeanaResultsDeferred::class => InvokableFactory::class,
         ExpandFacets::class => ExpandFacetsFactory::class,
+        ExternalSearch::class => InvokableFactory::class,
         FacetCloud::class => InvokableFactory::class,
         FavoriteFacets::class => FavoriteFacetsFactory::class,
         Libraryh3lp::class => InvokableFactory::class,
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExternalSearchTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExternalSearchTest.php
new file mode 100644
index 00000000000..092f7a97242
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExternalSearchTest.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * ExternalSearch recommendation module Test Class
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2019.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  Tests
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
+ */
+namespace VuFindTest\Recommend;
+
+use VuFind\Recommend\ExternalSearch;
+
+/**
+ * ExternalSearch recommendation module Test Class
+ *
+ * @category VuFind
+ * @package  Tests
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
+ */
+class ExternalSearchTest extends \VuFindTest\Unit\TestCase
+{
+    /**
+     * Run a test scenario
+     *
+     * @param string $label       Link text
+     * @param string $template    Link template
+     * @param string $lookfor     Search query
+     * @param string $expectedUrl Expected URL
+     *
+     * @return void
+     */
+    protected function runProcedure($label, $template, $lookfor, $expectedUrl)
+    {
+        $rec = new ExternalSearch();
+        $rec->setConfig($label . ':' . $template);
+        $params = new \Zend\StdLib\Parameters();
+        $params->set('lookfor', $lookfor);
+        $rec->init(
+            $this->createMock(\VuFind\Search\Solr\Params::class),
+            $params
+        );
+        $rec->process(
+            $this->createMock(\VuFind\Search\Solr\Results::class)
+        );
+        $this->assertEquals($label, $rec->getLinkText());
+        $this->assertEquals($expectedUrl, $rec->getUrl());
+    }
+
+    /**
+     * Test concatenation behavior
+     *
+     * @return void
+     */
+    public function testDefaultConcatenation()
+    {
+        $this->runProcedure(
+            'my label',
+            'http://foo?q=',
+            'beep',
+            'http://foo?q=beep'
+        );
+    }
+
+    /**
+     * Test template insertion behavior
+     *
+     * @return void
+     */
+    public function testTemplateBehavior()
+    {
+        $this->runProcedure(
+            'my label',
+            'http://foo?q=%%lookfor%%&z=xyzzy',
+            'beep',
+            'http://foo?q=beep&z=xyzzy'
+        );
+    }
+}
diff --git a/themes/bootstrap3/templates/Recommend/ExternalSearch.phtml b/themes/bootstrap3/templates/Recommend/ExternalSearch.phtml
new file mode 100644
index 00000000000..4d787777193
--- /dev/null
+++ b/themes/bootstrap3/templates/Recommend/ExternalSearch.phtml
@@ -0,0 +1,3 @@
+<div class="alert alert-info">
+  <a href="<?=$this->recommend->getUrl()?>"><?=$this->transEsc($this->recommend->getLinkText())?></a>
+</div>
-- 
GitLab