From f916dba5ee95be21b1b506ccc663fdaf2b3af234 Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Wed, 10 Jul 2013 11:46:28 -0400
Subject: [PATCH] Created mechanism for combined handler drop-down. Resolves
 VUFIND-843.

---
 config/vufind/searchbox.ini                   |  25 ++++
 module/VuFind/config/module.config.php        |   2 +-
 .../VuFind/Controller/CombinedController.php  |  34 +++++
 .../src/VuFind/View/Helper/Root/SearchBox.php | 116 +++++++++++++++++-
 .../templates/search/searchbox.phtml          |  11 +-
 .../templates/search/searchbox.phtml          |  11 +-
 themes/root/theme.config.php                  |   6 +-
 7 files changed, 190 insertions(+), 15 deletions(-)
 create mode 100644 config/vufind/searchbox.ini

diff --git a/config/vufind/searchbox.ini b/config/vufind/searchbox.ini
new file mode 100644
index 00000000000..365da2ad6e1
--- /dev/null
+++ b/config/vufind/searchbox.ini
@@ -0,0 +1,25 @@
+; This file controls VuFind's search box
+
+[General]
+; Set this to true in order to use a combined search handler drop-down as specified
+; in the [CombinedHandlers] section below.
+combinedHandlers = false
+
+; This section controls the "combined handlers" drop-down. It must contain groups
+; of settings with the following keys:
+;
+; type[] = "VuFind" for an internal search module or "External" for an external URL
+; target[] = Search class ID for "VuFind" type, URL for "External" type
+; label[] = Label for this value (subject to translation)
+[CombinedHandlers]
+type[] = VuFind
+target[] = Solr
+label[] = Catalog
+
+type[] = VuFind
+target[] = Summon
+label[] = Summon
+
+type[] = External
+target[] = "http://www.google.com/search?q="
+label[] = Google
diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index 1143b64b82c..846c537fbca 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -974,7 +974,7 @@ $staticRoutes = array(
     'Browse/LCC', 'Browse/Region', 'Browse/Tag', 'Browse/Topic',
     'Cart/doExport', 'Cart/Email', 'Cart/Export', 'Cart/Home', 'Cart/MyResearchBulk',
     'Cart/Save', 'Collections/ByTitle', 'Collections/Home',
-    'Combined/Home', 'Combined/Results', 'Confirm/Confirm',
+    'Combined/Home', 'Combined/Results', 'Combined/SearchBox', 'Confirm/Confirm',
     'Cover/Show', 'Cover/Unavailable', 'Error/Unavailable',
     'Feedback/Email', 'Feedback/Home', 'Help/Home',
     'Install/Done', 'Install/FixBasicConfig', 'Install/FixCache',
diff --git a/module/VuFind/src/VuFind/Controller/CombinedController.php b/module/VuFind/src/VuFind/Controller/CombinedController.php
index 502c4d05303..bade60b4624 100644
--- a/module/VuFind/src/VuFind/Controller/CombinedController.php
+++ b/module/VuFind/src/VuFind/Controller/CombinedController.php
@@ -137,4 +137,38 @@ class CombinedController extends AbstractSearch
             )
         );
     }
+
+    /**
+     * Action to process the combined search box.
+     *
+     * @return mixed
+     */
+    public function searchboxAction()
+    {
+        list($type, $target) = explode(':', $this->params()->fromQuery('type'), 2);
+        switch ($type) {
+        case 'VuFind':
+            list($searchClassId, $type) = explode('|', $target);
+            $params = $this->getRequest()->getQuery()->toArray();
+            $params['type'] = $type;
+
+            // Disable retained filters if we are switching classes!
+            $activeClass = $this->params()->fromQuery('activeSearchClassId');
+            if ($activeClass != $searchClassId) {
+                unset($params['filter']);
+            }
+            unset($params['activeSearchClassId']); // don't need to pass this forward
+
+            $route = $this->getServiceLocator()
+                ->get('VuFind\SearchOptionsPluginManager')
+                ->get($searchClassId)->getSearchAction();
+            $options = array('query' => $params);
+            return $this->redirect()->toRoute($route, array(), $options);
+        case 'External':
+            $lookfor = $this->params()->fromQuery('lookfor');
+            return $this->redirect()->toUrl($target . urlencode($lookfor));
+        default:
+            throw new \Exception('Unexpected search type.');
+        }
+    }
 }
diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php
index 963de2aa61e..6d6d88887d9 100644
--- a/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php
+++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php
@@ -26,6 +26,7 @@
  * @link     http://vufind.org/wiki/vufind2:developer_manual Wiki
  */
 namespace VuFind\View\Helper\Root;
+use VuFind\Search\Options\PluginManager as OptionsManager;
 
 /**
  * Search box view helper
@@ -38,6 +39,43 @@ namespace VuFind\View\Helper\Root;
  */
 class SearchBox extends \Zend\View\Helper\AbstractHelper
 {
+    /**
+     * Configuration for search box.
+     *
+     * @var array
+     */
+    protected $config;
+
+    /**
+     * Search options plugin manager
+     *
+     * @var OptionsManager
+     */
+    protected $optionsManager;
+
+    /**
+     * Constructor
+     *
+     * @param OptionsManager $optionsManager Search options plugin manager
+     * @param array          $config         Configuration for search box
+     */
+    public function __construct(OptionsManager $optionsManager, $config = array())
+    {
+        $this->optionsManager = $optionsManager;
+        $this->config = $config;
+    }
+
+    /**
+     * Are combined handlers enabled?
+     *
+     * @return bool
+     */
+    public function combinedHandlersActive()
+    {
+        return isset($this->config['General']['combinedHandlers'])
+            && $this->config['General']['combinedHandlers'];
+    }
+
     /**
      * Get an array of filter information for use by the "retain filters" feature
      * of the search box. Returns an array of arrays with 'id' and 'value' keys used
@@ -74,24 +112,92 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper
 
     /**
      * Get an array of information on search handlers for use in generating a
-     * drop-down or hidden field. Returns an array of arrays with 'value', 'label'
-     * and 'selected' keys.
+     * drop-down or hidden field. Returns an array of arrays with 'value', 'label',
+     * 'indent' and 'selected' keys.
      *
      * @param string                      $activeSearchClass Active search class ID
      * @param string                      $activeHandler     Active search handler
-     * @param \VuFind\Search\Base\Options $options           Current search options
      *
      * @return array
      */
-    public function getHandlers($activeSearchClass, $activeHandler, $options)
+    public function getHandlers($activeSearchClass, $activeHandler)
+    {
+        return $this->combinedHandlersActive()
+            ? $this->getCombinedHandlers($activeSearchClass, $activeHandler)
+            : $this->getBasicHandlers($activeSearchClass, $activeHandler);
+    }
+
+    /**
+     * Support method for getHandlers() -- load basic settings.
+     *
+     * @param string                      $activeSearchClass Active search class ID
+     * @param string                      $activeHandler     Active search handler
+     *
+     * @return array
+     */
+    protected function getBasicHandlers($activeSearchClass, $activeHandler)
     {
         $handlers = array();
+        $options = $this->optionsManager->get($activeSearchClass);
         foreach ($options->getBasicHandlers() as $searchVal => $searchDesc) {
             $handlers[] = array(
-                'value' => $searchVal, 'label' => $searchDesc,
+                'value' => $searchVal, 'label' => $searchDesc, 'indent' => false,
                 'selected' => ($activeHandler == $searchVal)
             );
         }
         return $handlers;
     }
+
+    /**
+     * Support method for getHandlers() -- load combined settings.
+     *
+     * @param string                      $activeSearchClass Active search class ID
+     * @param string                      $activeHandler     Active search handler
+     *
+     * @return array
+     */
+    protected function getCombinedHandlers($activeSearchClass, $activeHandler)
+    {
+        // Load and validate configuration:
+        $settings = isset($this->config['CombinedHandlers'])
+            ? $this->config['CombinedHandlers'] : array();
+        if (empty($settings)) {
+            throw new \Exception('CombinedHandlers configuration missing.');
+        }
+        $typeCount = count($settings['type']);
+        if ($typeCount != count($settings['target'])
+            || $typeCount != count($settings['label'])
+        ) {
+            throw new \Exception('CombinedHandlers configuration incomplete.');
+        }
+
+        // Build settings:
+        $handlers = array();
+        for ($i = 0; $i < $typeCount; $i++) {
+            $type = $settings['type'][$i];
+            $target = $settings['target'][$i];
+            $label = $settings['label'][$i];
+
+            if ($type == 'VuFind') {
+                $options = $this->optionsManager->get($target);
+                $j = 0;
+                foreach ($options->getBasicHandlers() as $searchVal => $searchDesc) {
+                    $j++;
+                    $handlers[] = array(
+                        'value' => $type . ':' . $target . '|' . $searchVal,
+                        'label' => $j == 1 ? $label : $searchDesc,
+                        'indent' => $j == 1 ? false : true,
+                        'selected' => $target == $activeSearchClass
+                            && $activeHandler == $searchVal
+                    );
+                }
+            } else if ($type == 'External') {
+                $handlers[] = array(
+                    'value' => $type . ':' . $target, 'label' => $label,
+                    'indent' => false, 'selected' => false
+                );
+            }
+        }
+        return $handlers;
+    }
 }
\ No newline at end of file
diff --git a/themes/blueprint/templates/search/searchbox.phtml b/themes/blueprint/templates/search/searchbox.phtml
index be08e4057a4..199232cf4a8 100644
--- a/themes/blueprint/templates/search/searchbox.phtml
+++ b/themes/blueprint/templates/search/searchbox.phtml
@@ -8,11 +8,10 @@
     $options = $this->searchOptions($this->searchClassId);
     $handlers = $this->searchbox()->getHandlers(
         $this->searchClassId,
-        isset($this->searchIndex) ? $this->searchIndex : null,
-        $options
+        isset($this->searchIndex) ? $this->searchIndex : null
     );
     $handlerCount = count($handlers);
-    $basicSearch = $options->getSearchAction();
+    $basicSearch = $this->searchbox()->combinedHandlersActive() ? 'combined-searchbox' : $options->getSearchAction();
     $searchHome = $options->getSearchHomeAction();
     $advSearch = $options->getAdvancedSearchAction();
     $lastSort = $options->getLastSort();
@@ -50,7 +49,7 @@
       <? if ($handlerCount > 1): ?>
         <select id="searchForm_type" name="type" data-native-menu="false">
           <? foreach ($handlers as $handler): ?>
-            <option value="<?=$this->escapeHtml($handler['value'])?>"<?=$handler['selected'] ? ' selected="selected"' : ''?>><?=$this->transEsc($handler['label'])?></option>
+            <option value="<?=$this->escapeHtml($handler['value'])?>"<?=$handler['selected'] ? ' selected="selected"' : ''?>><?=$handler['indent'] ? '-- ' : ''?><?=$this->transEsc($handler['label'])?></option>
           <? endforeach; ?>
         </select>
       <? elseif ($handlerCount == 1): ?>
@@ -91,6 +90,10 @@
         </div>
       <? endif; ?>
       <?
+      /* Show hidden field for active search class when in combined handler mode. */
+      if ($this->searchbox()->combinedHandlersActive()) {
+        echo '<input type="hidden" name="activeSearchClassId" value="' . $this->escapeHtml($this->searchClassId) . '" />';
+      }
       /* Load hidden limit preference from Session */
       if (!empty($lastLimit)) {
         echo '<input type="hidden" name="limit" value="' . $this->escapeHtml($lastLimit) . '" />';
diff --git a/themes/jquerymobile/templates/search/searchbox.phtml b/themes/jquerymobile/templates/search/searchbox.phtml
index efd8113dab8..875741a7b40 100644
--- a/themes/jquerymobile/templates/search/searchbox.phtml
+++ b/themes/jquerymobile/templates/search/searchbox.phtml
@@ -8,11 +8,10 @@
   $options = $this->searchOptions($this->searchClassId);
   $handlers = $this->searchbox()->getHandlers(
       $this->searchClassId,
-      isset($this->searchIndex) ? $this->searchIndex : null,
-      $options
+      isset($this->searchIndex) ? $this->searchIndex : null
   );
   $handlerCount = count($handlers);
-  $basicSearch = $options->getSearchAction();
+  $basicSearch = $this->searchbox()->combinedHandlersActive() ? 'combined-searchbox' : $options->getSearchAction();
   $lastSort = $options->getLastSort();
   $lastLimit = $options->getLastLimit();
 ?>
@@ -26,7 +25,7 @@
   <? if ($handlerCount > 1): ?>
     <select id="searchForm_type" name="type" data-native-menu="false">
       <? foreach ($handlers as $handler): ?>
-        <option value="<?=$this->escapeHtml($handler['value'])?>"<?=$handler['selected'] ? ' selected="selected"' : ''?>><?=$this->transEsc($handler['label'])?></option>
+        <option value="<?=$this->escapeHtml($handler['value'])?>"<?=$handler['selected'] ? ' selected="selected"' : ''?>><?=$handler['indent'] ? '-- ' : ''?><?=$this->transEsc($handler['label'])?></option>
       <? endforeach; ?>
     </select>
   <? elseif ($handlerCount == 1): ?>
@@ -36,6 +35,10 @@
     <input type="submit" data-theme="b" name="submit" value="<?=$this->transEsc("Find")?>"/>
   </div>
   <?
+    /* Show hidden field for active search class when in combined handler mode. */
+    if ($this->searchbox()->combinedHandlersActive()) {
+      echo '<input type="hidden" name="activeSearchClassId" value="' . $this->escapeHtml($this->searchClassId) . '" />';
+    }
     /* Load hidden limit preference from Session */
     if (!empty($lastLimit)) {
       echo '<input type="hidden" name="limit" value="' . $this->escapeHtml($lastLimit) . '" />';
diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php
index 101cfca5b49..8f705fdf906 100644
--- a/themes/root/theme.config.php
+++ b/themes/root/theme.config.php
@@ -104,7 +104,11 @@ return array(
                 );
             },
             'searchbox' => function ($sm) {
-                return new \VuFind\View\Helper\Root\SearchBox();
+                $config = $sm->getServiceLocator()->get('VuFind\Config')->get('searchbox')->toArray();
+                return new \VuFind\View\Helper\Root\SearchBox(
+                    $sm->getServiceLocator()->get('VuFind\SearchOptionsPluginManager'),
+                    $config
+                );
             },
             'searchoptions' => function ($sm) {
                 return new VuFind\View\Helper\Root\SearchOptions(
-- 
GitLab