Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
67.62% covered (warning)
67.62%
71 / 105
37.50% covered (danger)
37.50%
3 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Options
67.62% covered (warning)
67.62%
71 / 105
37.50% covered (danger)
37.50%
3 / 8
134.88
0.00% covered (danger)
0.00%
0 / 1
 __construct
69.39% covered (warning)
69.39%
68 / 98
0.00% covered (danger)
0.00%
0 / 1
92.60
 getSearchAction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAdvancedSearchAction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFacetListAction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getVersionsAction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getEmptySearchRelevanceOverride
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSortTieBreaker
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 supportsScheduledSearch
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Solr aspect of the Search Multi-class (Options)
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2011.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Search_Solr
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org Main Page
28 */
29
30namespace VuFind\Search\Solr;
31
32use function count;
33use function is_object;
34
35/**
36 * Solr Search Options
37 *
38 * @category VuFind
39 * @package  Search_Solr
40 * @author   Demian Katz <demian.katz@villanova.edu>
41 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
42 * @link     https://vufind.org Main Page
43 */
44class Options extends \VuFind\Search\Base\Options
45{
46    use \VuFind\Config\Feature\ExplodeSettingTrait;
47    use \VuFind\Search\Options\ViewOptionsTrait;
48
49    /**
50     * Available sort options for facets
51     *
52     * @var array
53     */
54    protected $facetSortOptions = [
55        '*' => ['count' => 'sort_count', 'index' => 'sort_alphabetic'],
56    ];
57
58    /**
59     * Relevance sort override for empty searches
60     *
61     * @var string
62     */
63    protected $emptySearchRelevanceOverride = null;
64
65    /**
66     * Whether to display record versions
67     *
68     * @var bool
69     */
70    protected $displayRecordVersions = true;
71
72    /**
73     * Solr field to be used as a tie-breaker.
74     *
75     * @var string
76     */
77    protected $sortTieBreaker = null;
78
79    /**
80     * Constructor
81     *
82     * @param \VuFind\Config\PluginManager $configLoader Config loader
83     */
84    public function __construct(\VuFind\Config\PluginManager $configLoader)
85    {
86        parent::__construct($configLoader);
87        $searchSettings = $configLoader->get($this->searchIni);
88        if (isset($searchSettings->General->default_limit)) {
89            $this->defaultLimit = $searchSettings->General->default_limit;
90        }
91        if (isset($searchSettings->General->limit_options)) {
92            $this->limitOptions = $this->explodeListSetting($searchSettings->General->limit_options);
93        }
94        if (isset($searchSettings->General->default_sort)) {
95            $this->defaultSort = $searchSettings->General->default_sort;
96        }
97        if (isset($searchSettings->General->tie_breaker_sort)) {
98            $this->sortTieBreaker = $searchSettings->General->tie_breaker_sort;
99        }
100        if (isset($searchSettings->General->empty_search_relevance_override)) {
101            $this->emptySearchRelevanceOverride
102                = $searchSettings->General->empty_search_relevance_override;
103        }
104        if (
105            isset($searchSettings->DefaultSortingByType)
106            && count($searchSettings->DefaultSortingByType) > 0
107        ) {
108            foreach ($searchSettings->DefaultSortingByType as $key => $val) {
109                $this->defaultSortByHandler[$key] = $val;
110            }
111        }
112        if (isset($searchSettings->RSS->sort)) {
113            $this->rssSort = $searchSettings->RSS->sort;
114        }
115        if (isset($searchSettings->General->default_handler)) {
116            $this->defaultHandler = $searchSettings->General->default_handler;
117        }
118        if (isset($searchSettings->General->default_filters)) {
119            $this->defaultFilters = $searchSettings->General->default_filters
120                ->toArray();
121        }
122        if (isset($searchSettings->General->display_versions)) {
123            $this->displayRecordVersions
124                = $searchSettings->General->display_versions;
125        }
126
127        // Result limit:
128        if (isset($searchSettings->General->result_limit)) {
129            $this->resultLimit = $searchSettings->General->result_limit;
130        }
131        if (isset($searchSettings->Basic_Searches)) {
132            foreach ($searchSettings->Basic_Searches as $key => $value) {
133                $this->basicHandlers[$key] = $value;
134            }
135        }
136        if (isset($searchSettings->Advanced_Searches)) {
137            foreach ($searchSettings->Advanced_Searches as $key => $value) {
138                $this->advancedHandlers[$key] = $value;
139            }
140        }
141
142        // Load sort preferences (or defaults if none in .ini file):
143        if (isset($searchSettings->Sorting)) {
144            foreach ($searchSettings->Sorting as $key => $value) {
145                $this->sortOptions[$key] = $value;
146            }
147        } else {
148            $this->sortOptions = ['relevance' => 'sort_relevance',
149                'year' => 'sort_year', 'year asc' => 'sort_year_asc',
150                'callnumber-sort' => 'sort_callnumber', 'author' => 'sort_author',
151                'title' => 'sort_title'];
152        }
153
154        // Set up views
155        $this->initViewOptions($searchSettings);
156
157        // Load list view for result (controls AJAX embedding vs. linking)
158        if (isset($searchSettings->List->view)) {
159            $this->listviewOption = $searchSettings->List->view;
160        }
161
162        // Load facet preferences
163        $facetSettings = $configLoader->get($this->facetsIni);
164        if (
165            isset($facetSettings->Advanced_Settings->translated_facets)
166            && count($facetSettings->Advanced_Settings->translated_facets) > 0
167        ) {
168            $this->setTranslatedFacets(
169                $facetSettings->Advanced_Settings->translated_facets->toArray()
170            );
171        }
172        if (isset($facetSettings->Advanced_Settings->delimiter)) {
173            $this->setDefaultFacetDelimiter(
174                $facetSettings->Advanced_Settings->delimiter
175            );
176        }
177        if (
178            isset($facetSettings->Advanced_Settings->delimited_facets)
179            && count($facetSettings->Advanced_Settings->delimited_facets) > 0
180        ) {
181            $this->setDelimitedFacets(
182                $facetSettings->Advanced_Settings->delimited_facets->toArray()
183            );
184        }
185        if (isset($facetSettings->Advanced_Settings->special_facets)) {
186            $this->specialAdvancedFacets
187                = $facetSettings->Advanced_Settings->special_facets;
188        }
189        if (isset($facetSettings->SpecialFacets->hierarchical)) {
190            $this->hierarchicalFacets
191                = $facetSettings->SpecialFacets->hierarchical->toArray();
192        }
193        if (isset($facetSettings->SpecialFacets->hierarchicalFacetSeparators)) {
194            $this->hierarchicalFacetSeparators = $facetSettings->SpecialFacets
195                ->hierarchicalFacetSeparators->toArray();
196        }
197        $this->hierarchicalFacetSortSettings
198            = $facetSettings?->SpecialFacets?->hierarchicalFacetSortOptions?->toArray() ?? [];
199
200        // Load Spelling preferences
201        $config = $configLoader->get($this->mainIni);
202        if (isset($config->Spelling->enabled)) {
203            $this->spellcheck = $config->Spelling->enabled;
204        }
205
206        // Turn on first/last navigation if configured:
207        if (
208            isset($config->Record->first_last_navigation)
209            && $config->Record->first_last_navigation
210        ) {
211            $this->recordPageFirstLastNavigation = true;
212        }
213
214        // Turn on highlighting if the user has requested highlighting or snippet
215        // functionality:
216        $highlight = $searchSettings->General->highlighting ?? false;
217        $snippet = $searchSettings->General->snippets ?? false;
218        if ($highlight || $snippet) {
219            $this->highlight = true;
220        }
221
222        // Load autocomplete preferences:
223        $this->configureAutocomplete($searchSettings);
224
225        // Load shard settings
226        if (
227            isset($searchSettings->IndexShards)
228            && !empty($searchSettings->IndexShards)
229        ) {
230            foreach ($searchSettings->IndexShards as $k => $v) {
231                $this->shards[$k] = $v;
232            }
233            // If we have a default from the configuration, use that...
234            if (
235                isset($searchSettings->ShardPreferences->defaultChecked)
236                && !empty($searchSettings->ShardPreferences->defaultChecked)
237            ) {
238                $defaultChecked
239                    = is_object($searchSettings->ShardPreferences->defaultChecked)
240                    ? $searchSettings->ShardPreferences->defaultChecked->toArray()
241                    : [$searchSettings->ShardPreferences->defaultChecked];
242                foreach ($defaultChecked as $current) {
243                    $this->defaultSelectedShards[] = $current;
244                }
245            } else {
246                // If no default is configured, use all shards...
247                $this->defaultSelectedShards = array_keys($this->shards);
248            }
249            // Apply checkbox visibility setting if applicable:
250            if (isset($searchSettings->ShardPreferences->showCheckboxes)) {
251                $this->visibleShardCheckboxes
252                    = $searchSettings->ShardPreferences->showCheckboxes;
253            }
254        }
255    }
256
257    /**
258     * Return the route name for the search results action.
259     *
260     * @return string
261     */
262    public function getSearchAction()
263    {
264        return 'search-results';
265    }
266
267    /**
268     * Return the route name of the action used for performing advanced searches.
269     * Returns false if the feature is not supported.
270     *
271     * @return string|bool
272     */
273    public function getAdvancedSearchAction()
274    {
275        return 'search-advanced';
276    }
277
278    /**
279     * Return the route name for the facet list action. Returns false to cover
280     * unimplemented support.
281     *
282     * @return string|bool
283     */
284    public function getFacetListAction()
285    {
286        return 'search-facetlist';
287    }
288
289    /**
290     * Return the route name for the versions search action or false if disabled.
291     *
292     * @return string|bool
293     */
294    public function getVersionsAction()
295    {
296        return $this->displayRecordVersions ? 'search-versions' : false;
297    }
298
299    /**
300     * Get the relevance sort override for empty searches.
301     *
302     * @return string Sort field or null if not set
303     */
304    public function getEmptySearchRelevanceOverride()
305    {
306        return $this->emptySearchRelevanceOverride;
307    }
308
309    /**
310     * Get the field to be used as a sort tie-breaker.
311     *
312     * @return ?string Sort field or null if not set
313     */
314    public function getSortTieBreaker()
315    {
316        return $this->sortTieBreaker;
317    }
318
319    /**
320     * Does this search backend support scheduled searching?
321     *
322     * @return bool
323     */
324    public function supportsScheduledSearch()
325    {
326        // Solr supports this!
327        return true;
328    }
329}