Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 343 |
|
0.00% |
0 / 23 |
CRAP | |
0.00% |
0 / 1 |
BrowseController | |
0.00% |
0 / 343 |
|
0.00% |
0 / 23 |
6642 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
setCurrentAction | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCurrentAction | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getActiveBrowseOptions | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
2 | |||
buildBrowseOptions | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
90 | |||
createViewModel | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
30 | |||
buildBrowseOption | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
homeAction | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
performSearch | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
90 | |||
tagAction | |
0.00% |
0 / 45 |
|
0.00% |
0 / 1 |
72 | |||
lccAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
deweyAction | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
20 | |||
performBrowse | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
authorAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
topicAction | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
genreAction | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
regionAction | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
eraAction | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getSecondaryList | |
0.00% |
0 / 44 |
|
0.00% |
0 / 1 |
72 | |||
getFacetList | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
42 | |||
quoteValues | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getCategory | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
110 | |||
getAlphabetList | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | /** |
4 | * Browse Module Controller |
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 Controller |
25 | * @author Chris Hallberg <challber@villanova.edu> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development:plugins:controllers Wiki |
28 | */ |
29 | |
30 | namespace VuFind\Controller; |
31 | |
32 | use Laminas\Config\Config; |
33 | use Laminas\ServiceManager\ServiceLocatorInterface; |
34 | use VuFind\Exception\Forbidden as ForbiddenException; |
35 | use VuFind\Tags\TagsService; |
36 | |
37 | use function array_slice; |
38 | use function in_array; |
39 | |
40 | /** |
41 | * BrowseController Class |
42 | * |
43 | * Controls the alphabetical browsing feature |
44 | * |
45 | * @category VuFind |
46 | * @package Controller |
47 | * @author Chris Hallberg <challber@villanova.edu> |
48 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
49 | * @link https://vufind.org/wiki/development:plugins:controllers Wiki |
50 | */ |
51 | class BrowseController extends AbstractBase implements |
52 | \VuFind\I18n\HasSorterInterface |
53 | { |
54 | use \VuFind\I18n\HasSorterTrait; |
55 | |
56 | /** |
57 | * VuFind configuration |
58 | * |
59 | * @var \Laminas\Config\Config |
60 | */ |
61 | protected $config; |
62 | |
63 | /** |
64 | * Current browse mode |
65 | * |
66 | * @var string |
67 | */ |
68 | protected $currentAction = null; |
69 | |
70 | /** |
71 | * Browse options disabled in configuration |
72 | * |
73 | * @var array |
74 | */ |
75 | protected $disabledFacets; |
76 | |
77 | /** |
78 | * Constructor |
79 | * |
80 | * @param ServiceLocatorInterface $sm Service manager |
81 | * @param Config $config VuFind configuration |
82 | */ |
83 | public function __construct(ServiceLocatorInterface $sm, Config $config) |
84 | { |
85 | $this->config = $config; |
86 | |
87 | $this->disabledFacets = []; |
88 | foreach ($this->config->Browse as $key => $setting) { |
89 | if ($setting == false) { |
90 | $this->disabledFacets[] = $key; |
91 | } |
92 | } |
93 | $this->setSorter($sm->get(\VuFind\I18n\Sorter::class)); |
94 | parent::__construct($sm); |
95 | } |
96 | |
97 | /** |
98 | * Set the name of the current action. |
99 | * |
100 | * @param string $name Name of the current action |
101 | * |
102 | * @return void |
103 | */ |
104 | protected function setCurrentAction($name) |
105 | { |
106 | $this->currentAction = $name; |
107 | } |
108 | |
109 | /** |
110 | * Get the name of the current action. |
111 | * |
112 | * @return string |
113 | */ |
114 | protected function getCurrentAction() |
115 | { |
116 | return $this->currentAction; |
117 | } |
118 | |
119 | /** |
120 | * Determine which browse options to display, and in which order. Returns an |
121 | * array of browse options in the configured order. |
122 | * |
123 | * @return array |
124 | */ |
125 | protected function getActiveBrowseOptions() |
126 | { |
127 | // Get a list of all of the options mentioned in config.ini: |
128 | $browseConfig = $this->config->Browse->toArray(); |
129 | $configuredOptions = array_keys($browseConfig); |
130 | |
131 | // This is a list of all available browse options: |
132 | $allOptions = [ |
133 | 'tag', 'dewey', 'lcc', 'author', 'topic', 'genre', 'region', 'era', |
134 | ]; |
135 | |
136 | // By default, all options except dewey are turned on if omitted from config: |
137 | $defaultOptions = array_diff($allOptions, ['dewey']); |
138 | |
139 | // This is a callback function for array_filter, which will filter out any |
140 | // settings set to false in config.ini: |
141 | $filter = function ($option) use ($browseConfig) { |
142 | return (bool)($browseConfig[$option] ?? false); |
143 | }; |
144 | |
145 | // The active options are a list of configured settings set to true in |
146 | // config.ini, merged with any default options that were not configured in |
147 | // config.ini at all: |
148 | return array_merge( |
149 | array_filter(array_intersect($configuredOptions, $allOptions), $filter), |
150 | array_diff($defaultOptions, $configuredOptions) |
151 | ); |
152 | } |
153 | |
154 | /** |
155 | * Given a list of active options, format them into details for the view. |
156 | * |
157 | * @return array |
158 | */ |
159 | protected function buildBrowseOptions() |
160 | { |
161 | // Initialize the array of top-level browse options. |
162 | $browseOptions = []; |
163 | |
164 | $activeOptions = $this->getActiveBrowseOptions(); |
165 | foreach ($activeOptions as $option) { |
166 | switch ($option) { |
167 | case 'dewey': |
168 | $deweyLabel = in_array('lcc', $activeOptions) |
169 | ? 'browse_dewey' : 'Call Number'; |
170 | $browseOptions[] = $this |
171 | ->buildBrowseOption('Dewey', $deweyLabel); |
172 | break; |
173 | case 'lcc': |
174 | $lccLabel = in_array('dewey', $activeOptions) |
175 | ? 'browse_lcc' : 'Call Number'; |
176 | $browseOptions[] = $this->buildBrowseOption('LCC', $lccLabel); |
177 | break; |
178 | case 'tag': |
179 | if ($this->tagsEnabled()) { |
180 | $browseOptions[] = $this->buildBrowseOption('Tag', 'Tag'); |
181 | } |
182 | break; |
183 | default: |
184 | $current = ucwords($option); |
185 | $browseOptions[] = $this->buildBrowseOption($current, $current); |
186 | break; |
187 | } |
188 | } |
189 | |
190 | return $browseOptions; |
191 | } |
192 | |
193 | /** |
194 | * Create a new ViewModel. |
195 | * |
196 | * @param array $params Parameters to pass to ViewModel constructor. |
197 | * |
198 | * @return \Laminas\View\Model\ViewModel |
199 | */ |
200 | protected function createViewModel($params = null) |
201 | { |
202 | $view = parent::createViewModel($params); |
203 | |
204 | // Set the current action. |
205 | $currentAction = $this->getCurrentAction(); |
206 | if (!empty($currentAction)) { |
207 | $view->currentAction = $currentAction; |
208 | } |
209 | |
210 | // CARRY |
211 | if ($findby = $this->params()->fromQuery('findby')) { |
212 | $view->findby = $findby; |
213 | } |
214 | if ($query = $this->params()->fromQuery('query')) { |
215 | $view->query = $query; |
216 | } |
217 | if ($category = $this->params()->fromQuery('category')) { |
218 | $view->category = $category; |
219 | } |
220 | $view->browseOptions = $this->buildBrowseOptions(); |
221 | |
222 | return $view; |
223 | } |
224 | |
225 | /** |
226 | * Build an array containing options describing a top-level Browse option. |
227 | * |
228 | * @param string $action The name of the Action for this option |
229 | * @param string $description A description of this Browse option |
230 | * |
231 | * @return array The Browse option array |
232 | */ |
233 | protected function buildBrowseOption($action, $description) |
234 | { |
235 | return ['action' => $action, 'description' => $description]; |
236 | } |
237 | |
238 | /** |
239 | * Gathers data for the view of the AlphaBrowser and does some initialization |
240 | * |
241 | * @return \Laminas\View\Model\ViewModel |
242 | */ |
243 | public function homeAction() |
244 | { |
245 | $this->setCurrentAction('Home'); |
246 | return $this->createViewModel(); |
247 | } |
248 | |
249 | /** |
250 | * Perform the search |
251 | * |
252 | * @param \Laminas\View\Model\ViewModel $view View model to modify |
253 | * |
254 | * @return \Laminas\View\Model\ViewModel |
255 | */ |
256 | protected function performSearch($view) |
257 | { |
258 | // Remove disabled facets |
259 | $facets = $view->categoryList; |
260 | foreach ($this->disabledFacets as $facet) { |
261 | unset($facets[$facet]); |
262 | } |
263 | $view->categoryList = $facets; |
264 | |
265 | // SEARCH (Tag does its own search) |
266 | if ( |
267 | $this->params()->fromQuery('query') |
268 | && $this->getCurrentAction() != 'Tag' |
269 | ) { |
270 | $results = $this->getFacetList( |
271 | $this->params()->fromQuery('facet_field'), |
272 | $this->params()->fromQuery('query_field'), |
273 | 'count', |
274 | $this->params()->fromQuery('query') |
275 | ); |
276 | $resultList = []; |
277 | foreach ($results as $result) { |
278 | $resultList[] = [ |
279 | 'displayText' => $result['displayText'], |
280 | 'value' => $result['value'], |
281 | 'count' => $result['count'], |
282 | ]; |
283 | } |
284 | // Don't make a second filter if it would be the same facet |
285 | $view->paramTitle |
286 | = ($this->params()->fromQuery('query_field') != $this->getCategory()) |
287 | ? 'filter[]=' . $this->params()->fromQuery('query_field') . ':' |
288 | . urlencode($this->params()->fromQuery('query')) . '&' |
289 | : ''; |
290 | switch ($this->getCurrentAction()) { |
291 | case 'LCC': |
292 | $view->paramTitle .= 'filter[]=callnumber-subject:'; |
293 | break; |
294 | case 'Dewey': |
295 | $view->paramTitle .= 'filter[]=dewey-ones:'; |
296 | break; |
297 | default: |
298 | $view->paramTitle .= 'filter[]=' . $this->getCategory() . ':'; |
299 | } |
300 | $view->paramTitle = str_replace( |
301 | '+AND+', |
302 | '&filter[]=', |
303 | $view->paramTitle |
304 | ); |
305 | $view->resultList = $resultList; |
306 | } |
307 | |
308 | $view->setTemplate('browse/home'); |
309 | return $view; |
310 | } |
311 | |
312 | /** |
313 | * Browse tags |
314 | * |
315 | * @return \Laminas\View\Model\ViewModel |
316 | */ |
317 | public function tagAction() |
318 | { |
319 | if (!$this->tagsEnabled()) { |
320 | throw new ForbiddenException('Tags disabled.'); |
321 | } |
322 | |
323 | $this->setCurrentAction('Tag'); |
324 | $view = $this->createViewModel(); |
325 | |
326 | $view->categoryList = [ |
327 | 'alphabetical' => 'By Alphabetical', |
328 | 'popularity' => 'By Popularity', |
329 | 'recent' => 'By Recent', |
330 | ]; |
331 | |
332 | if ($this->params()->fromQuery('findby')) { |
333 | $params = $this->getRequest()->getQuery()->toArray(); |
334 | $tagsService = $this->getService(TagsService::class); |
335 | // Special case -- display alphabet selection if necessary: |
336 | if ($params['findby'] == 'alphabetical') { |
337 | $legalLetters = $this->getAlphabetList(); |
338 | $view->secondaryList = $legalLetters; |
339 | // Only display tag list when a valid letter is selected: |
340 | if (isset($params['query'])) { |
341 | // Note -- this does not need to be escaped because |
342 | // $params['query'] has already been validated against |
343 | // the getAlphabetList() method below! |
344 | $tags = $tagsService->getNonListTagsFuzzilyMatchingString($params['query']); |
345 | $tagList = []; |
346 | foreach ($tags as $tag) { |
347 | if ($tag['cnt'] > 0) { |
348 | $tagList[] = [ |
349 | 'displayText' => $tag['tag'], |
350 | 'value' => $tag['tag'], |
351 | 'count' => $tag['cnt'], |
352 | ]; |
353 | } |
354 | } |
355 | $view->resultList = array_slice( |
356 | $tagList, |
357 | 0, |
358 | $this->config->Browse->result_limit |
359 | ); |
360 | } |
361 | } else { |
362 | // Default case: always display tag list for non-alphabetical modes: |
363 | $tagList = $tagsService->getTagBrowseList( |
364 | $params['findby'], |
365 | (int)($this->config->Browse->result_limit ?? 100) |
366 | ); |
367 | $resultList = []; |
368 | foreach ($tagList as $i => $tag) { |
369 | $resultList[$i] = [ |
370 | 'displayText' => $tag['tag'], |
371 | 'value' => $tag['tag'], |
372 | 'count' => $tag['cnt'], |
373 | ]; |
374 | } |
375 | $view->resultList = $resultList; |
376 | } |
377 | $view->paramTitle = 'lookfor='; |
378 | $view->searchParams = []; |
379 | } |
380 | |
381 | return $this->performSearch($view); |
382 | } |
383 | |
384 | /** |
385 | * Browse LCC |
386 | * |
387 | * @return \Laminas\View\Model\ViewModel |
388 | */ |
389 | public function lccAction() |
390 | { |
391 | $this->setCurrentAction('LCC'); |
392 | $view = $this->createViewModel(); |
393 | [$view->filter, $view->secondaryList] = $this->getSecondaryList('lcc'); |
394 | $view->secondaryParams = [ |
395 | 'query_field' => 'callnumber-first', |
396 | 'facet_field' => 'callnumber-subject', |
397 | ]; |
398 | $view->searchParams = ['sort' => 'callnumber-sort']; |
399 | return $this->performSearch($view); |
400 | } |
401 | |
402 | /** |
403 | * Browse Dewey |
404 | * |
405 | * @return \Laminas\View\Model\ViewModel |
406 | */ |
407 | public function deweyAction() |
408 | { |
409 | $this->setCurrentAction('Dewey'); |
410 | $view = $this->createViewModel(); |
411 | [$view->filter, $hundredsList] = $this->getSecondaryList('dewey'); |
412 | $categoryList = []; |
413 | foreach ($hundredsList as $dewey) { |
414 | $categoryList[$dewey['value']] = [ |
415 | 'text' => $dewey['displayText'], |
416 | 'count' => $dewey['count'], |
417 | ]; |
418 | } |
419 | $view->categoryList = $categoryList; |
420 | $view->dewey_flag = 1; |
421 | if ($this->params()->fromQuery('findby')) { |
422 | $secondaryList = $this->quoteValues( |
423 | $this->getFacetList( |
424 | 'dewey-tens', |
425 | 'dewey-hundreds', |
426 | 'count', |
427 | $this->params()->fromQuery('findby') |
428 | ) |
429 | ); |
430 | foreach (array_keys($secondaryList) as $index) { |
431 | $secondaryList[$index]['value'] .= |
432 | ' AND dewey-hundreds:' |
433 | . $this->params()->fromQuery('findby'); |
434 | } |
435 | $view->secondaryList = $secondaryList; |
436 | $view->secondaryParams = [ |
437 | 'query_field' => 'dewey-tens', |
438 | 'facet_field' => 'dewey-ones', |
439 | ]; |
440 | $view->searchParams = ['sort' => 'dewey-sort']; |
441 | } |
442 | return $this->performSearch($view); |
443 | } |
444 | |
445 | /** |
446 | * Generic action function that handles all the common parts of the below actions |
447 | * |
448 | * @param string $currentAction name of the current action. profound stuff. |
449 | * @param array $categoryList category options |
450 | * @param string $facetPrefix if this is true and we're looking |
451 | * alphabetically, add a facet_prefix to the URL |
452 | * |
453 | * @return \Laminas\View\Model\ViewModel |
454 | */ |
455 | protected function performBrowse($currentAction, $categoryList, $facetPrefix) |
456 | { |
457 | $this->setCurrentAction($currentAction); |
458 | $view = $this->createViewModel(); |
459 | $view->categoryList = $categoryList; |
460 | |
461 | $findby = $this->params()->fromQuery('findby'); |
462 | if ($findby) { |
463 | $view->secondaryParams = [ |
464 | 'query_field' => $this->getCategory($findby), |
465 | 'facet_field' => $this->getCategory($currentAction), |
466 | ]; |
467 | $view->facetPrefix = $facetPrefix && $findby == 'alphabetical'; |
468 | [$view->filter, $view->secondaryList] |
469 | = $this->getSecondaryList($findby); |
470 | } |
471 | |
472 | return $this->performSearch($view); |
473 | } |
474 | |
475 | /** |
476 | * Browse Author |
477 | * |
478 | * @return \Laminas\View\Model\ViewModel |
479 | */ |
480 | public function authorAction() |
481 | { |
482 | $categoryList = [ |
483 | 'alphabetical' => 'By Alphabetical', |
484 | 'lcc' => 'By Call Number', |
485 | 'topic' => 'By Topic', |
486 | 'genre' => 'By Genre', |
487 | 'region' => 'By Region', |
488 | 'era' => 'By Era', |
489 | ]; |
490 | |
491 | return $this->performBrowse('Author', $categoryList, true); |
492 | } |
493 | |
494 | /** |
495 | * Browse Topic |
496 | * |
497 | * @return \Laminas\View\Model\ViewModel |
498 | */ |
499 | public function topicAction() |
500 | { |
501 | $categoryList = [ |
502 | 'alphabetical' => 'By Alphabetical', |
503 | 'genre' => 'By Genre', |
504 | 'region' => 'By Region', |
505 | 'era' => 'By Era', |
506 | ]; |
507 | |
508 | return $this->performBrowse('Topic', $categoryList, true); |
509 | } |
510 | |
511 | /** |
512 | * Browse Genre |
513 | * |
514 | * @return \Laminas\View\Model\ViewModel |
515 | */ |
516 | public function genreAction() |
517 | { |
518 | $categoryList = [ |
519 | 'alphabetical' => 'By Alphabetical', |
520 | 'topic' => 'By Topic', |
521 | 'region' => 'By Region', |
522 | 'era' => 'By Era', |
523 | ]; |
524 | |
525 | return $this->performBrowse('Genre', $categoryList, true); |
526 | } |
527 | |
528 | /** |
529 | * Browse Region |
530 | * |
531 | * @return \Laminas\View\Model\ViewModel |
532 | */ |
533 | public function regionAction() |
534 | { |
535 | $categoryList = [ |
536 | 'alphabetical' => 'By Alphabetical', |
537 | 'topic' => 'By Topic', |
538 | 'genre' => 'By Genre', |
539 | 'era' => 'By Era', |
540 | ]; |
541 | |
542 | return $this->performBrowse('Region', $categoryList, true); |
543 | } |
544 | |
545 | /** |
546 | * Browse Era |
547 | * |
548 | * @return \Laminas\View\Model\ViewModel |
549 | */ |
550 | public function eraAction() |
551 | { |
552 | $categoryList = [ |
553 | 'alphabetical' => 'By Alphabetical', |
554 | 'topic' => 'By Topic', |
555 | 'genre' => 'By Genre', |
556 | 'region' => 'By Region', |
557 | ]; |
558 | |
559 | return $this->performBrowse('Era', $categoryList, true); |
560 | } |
561 | |
562 | /** |
563 | * Get array with two values: a filter name and a secondary list based on facets |
564 | * |
565 | * @param string $facet the facet we need the contents of |
566 | * |
567 | * @return array |
568 | */ |
569 | protected function getSecondaryList($facet) |
570 | { |
571 | $category = $this->getCategory(); |
572 | switch ($facet) { |
573 | case 'alphabetical': |
574 | return ['', $this->getAlphabetList()]; |
575 | case 'dewey': |
576 | return [ |
577 | 'dewey-tens', $this->quoteValues( |
578 | $this->getFacetList('dewey-hundreds', $category, 'index') |
579 | ), |
580 | ]; |
581 | case 'lcc': |
582 | return [ |
583 | 'callnumber-first', $this->quoteValues( |
584 | $this->getFacetList( |
585 | 'callnumber-first', |
586 | $category, |
587 | 'index' |
588 | ) |
589 | ), |
590 | ]; |
591 | case 'topic': |
592 | return [ |
593 | 'topic_facet', $this->quoteValues( |
594 | $this->getFacetList('topic_facet', $category) |
595 | ), |
596 | ]; |
597 | case 'genre': |
598 | return [ |
599 | 'genre_facet', $this->quoteValues( |
600 | $this->getFacetList('genre_facet', $category) |
601 | ), |
602 | ]; |
603 | case 'region': |
604 | return [ |
605 | 'geographic_facet', $this->quoteValues( |
606 | $this->getFacetList('geographic_facet', $category) |
607 | ), |
608 | ]; |
609 | case 'era': |
610 | return [ |
611 | 'era_facet', $this->quoteValues( |
612 | $this->getFacetList('era_facet', $category) |
613 | ), |
614 | ]; |
615 | } |
616 | throw new \Exception('Unexpected value: ' . $facet); |
617 | } |
618 | |
619 | /** |
620 | * Get a list of items from a facet. |
621 | * |
622 | * @param string $facet which facet we're searching in |
623 | * @param string $category which subfacet the search applies to |
624 | * @param string $sort how are we ranking these? || 'index' |
625 | * @param string $query is there a specific query? No = wildcard |
626 | * |
627 | * @return array Array indexed by value with text of displayText and |
628 | * count |
629 | */ |
630 | protected function getFacetList( |
631 | $facet, |
632 | $category = null, |
633 | $sort = 'count', |
634 | $query = '[* TO *]' |
635 | ) { |
636 | $results = $this->getService(\VuFind\Search\Results\PluginManager::class)->get('Solr'); |
637 | $params = $results->getParams(); |
638 | $params->addFacet($facet); |
639 | if ($category != null) { |
640 | $query = $category . ':' . $query; |
641 | } else { |
642 | $query = $facet . ':' . $query; |
643 | } |
644 | $params->setOverrideQuery($query); |
645 | $params->getOptions()->disableHighlighting(); |
646 | $params->getOptions()->spellcheckEnabled(false); |
647 | // Get limit from config |
648 | $params->setFacetLimit($this->config->Browse->result_limit); |
649 | $params->setLimit(0); |
650 | // Facet prefix |
651 | if ($this->params()->fromQuery('facet_prefix')) { |
652 | $params->setFacetPrefix($this->params()->fromQuery('facet_prefix')); |
653 | } |
654 | $params->setFacetSort($sort); |
655 | $result = $results->getFacetList(); |
656 | if (isset($result[$facet])) { |
657 | // Sort facets alphabetically if configured to do so: |
658 | if ( |
659 | isset($this->config->Browse->alphabetical_order) |
660 | && $this->config->Browse->alphabetical_order |
661 | ) { |
662 | $callback = function ($a, $b) { |
663 | return $this->getSorter()->compare( |
664 | $a['displayText'], |
665 | $b['displayText'] |
666 | ); |
667 | }; |
668 | usort($result[$facet]['list'], $callback); |
669 | } |
670 | return $result[$facet]['list']; |
671 | } else { |
672 | return []; |
673 | } |
674 | } |
675 | |
676 | /** |
677 | * Helper class that adds quotes around the values of an array |
678 | * |
679 | * @param array $array Two-dimensional array where each entry has a value param |
680 | * |
681 | * @return array Array indexed by value with text of displayText and count |
682 | */ |
683 | protected function quoteValues($array) |
684 | { |
685 | foreach ($array as $i => $result) { |
686 | $result['value'] = '"' . $result['value'] . '"'; |
687 | $array[$i] = $result; |
688 | } |
689 | return $array; |
690 | } |
691 | |
692 | /** |
693 | * Get the facet search term for an action |
694 | * |
695 | * @param string $action action to be translated |
696 | * |
697 | * @return string |
698 | */ |
699 | protected function getCategory($action = null) |
700 | { |
701 | if ($action == null) { |
702 | $action = $this->getCurrentAction(); |
703 | } |
704 | switch (strtolower($action)) { |
705 | case 'alphabetical': |
706 | return $this->getCategory(); |
707 | case 'dewey': |
708 | return 'dewey-hundreds'; |
709 | case 'lcc': |
710 | return 'callnumber-first'; |
711 | case 'author': |
712 | return 'author_facet'; |
713 | case 'topic': |
714 | return 'topic_facet'; |
715 | case 'genre': |
716 | return 'genre_facet'; |
717 | case 'region': |
718 | return 'geographic_facet'; |
719 | case 'era': |
720 | return 'era_facet'; |
721 | } |
722 | return $action; |
723 | } |
724 | |
725 | /** |
726 | * Get a list of letters to display in alphabetical mode. |
727 | * |
728 | * @return array |
729 | */ |
730 | protected function getAlphabetList() |
731 | { |
732 | // Get base alphabet: |
733 | $chars = $this->config->Browse->alphabet_letters |
734 | ?? 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
735 | |
736 | // Put numbers in the front for Era since years are important: |
737 | if ($this->getCurrentAction() == 'Era') { |
738 | $chars = '0123456789' . $chars; |
739 | } else { |
740 | $chars .= '0123456789'; |
741 | } |
742 | |
743 | // ALPHABET TO ['value','displayText'] |
744 | // (value has asterisk appended for Solr, but is unmodified for tags) |
745 | $action = $this->getCurrentAction(); |
746 | $callback = function ($letter) use ($action) { |
747 | // Tag is a special case because it is database-backed; for everything |
748 | // else, use a Solr query that will allow case-insensitive lookups. |
749 | $value = ($action == 'Tag') |
750 | ? $letter |
751 | : '(' . strtoupper($letter) . '* OR ' . strtolower($letter) . '*)'; |
752 | return ['value' => $value, 'displayText' => $letter]; |
753 | }; |
754 | preg_match_all('/(.)/u', $chars, $matches); |
755 | return array_map($callback, $matches[1]); |
756 | } |
757 | } |