When someone clicks an item from the Solr search results, Mirador opens with the search term already run against the item's IIIF content search service, so matches are highlighted with no extra JavaScript.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.5 KiB

<?php
/**
* @file
* Runs a IIIF content search in Mirador on load from a passed search term.
*/
use Drupal\advanced_search\AdvancedSearchQuery;
use Drupal\advanced_search\AdvancedSearchQueryTerm;
use Drupal\views\ViewExecutable;
/**
* Query string parameter used to carry the search term to the item page.
*
* Matches the identifier used by the search_in_hocr view.
*/
const MIRADOR_SEARCH_HIGHLIGHT_PARAM = 'search_api_fulltext';
/**
* Views whose item links should carry the search term.
*/
const MIRADOR_SEARCH_HIGHLIGHT_VIEWS = ['solr_search_content'];
/**
* Implements hook_views_pre_render().
*
* Appends the search term to "Link this field to its item" links so the item
* page knows what was searched for.
*/
function mirador_search_highlight_views_pre_render(ViewExecutable $view) {
if (!in_array($view->id(), MIRADOR_SEARCH_HIGHLIGHT_VIEWS, TRUE)) {
return;
}
$keys = _mirador_search_highlight_get_keys($view);
if ($keys === '') {
return;
}
foreach ($view->field as $handler) {
if (!empty($handler->options['link_to_item'])) {
// Search API items are merged with $options['alter'] when rendered as
// links, and an explicit 'query' replaces the item URL's query.
// UrlHelper::buildQuery() encodes the value before Views token
// replacement, so user input is never treated as Twig.
$handler->options['alter']['query'] = [MIRADOR_SEARCH_HIGHLIGHT_PARAM => $keys];
}
}
}
/**
* Gets the search term the view was run with.
*
* @param \Drupal\views\ViewExecutable $view
* The search view.
*
* @return string
* The search term, or an empty string if there is none.
*/
function _mirador_search_highlight_get_keys(ViewExecutable $view) {
// Advanced Search passes terms as a[N][v] and writes them straight into the
// Solr query, so the Search API query never has keys set. Read the terms
// the same way it does, skipping excluded ("IS NOT") terms.
if (class_exists(AdvancedSearchQuery::class) && ($request = $view->getRequest())) {
$values = [];
foreach ((new AdvancedSearchQuery())->getTerms($request) as $term) {
if ($term->getInclude()) {
$values[] = trim((string) ($term->toQueryParams()[AdvancedSearchQueryTerm::VALUE_QUERY_PARAMETER] ?? ''));
}
}
$keys = implode(' ', array_filter($values, 'strlen'));
if ($keys !== '') {
return $keys;
}
}
// Fall back to a plain Search API fulltext filter. Parse modes may turn the
// keys into an array; only the raw string is useful to Mirador.
if (method_exists($view->query, 'getOriginalKeys')) {
$keys = $view->query->getOriginalKeys();
if (is_string($keys)) {
return trim($keys);
}
}
return '';
}
/**
* Implements hook_preprocess_HOOK() for mirador.
*
* Runs after template_preprocess_mirador(), so mirador_view_id is already the
* unique ID used as the drupalSettings viewer key.
*/
function mirador_search_highlight_preprocess_mirador(&$variables) {
// Vary the cached viewer by the search term, otherwise the first term
// searched would be cached and shown to everyone viewing this item.
$variables['#cache']['contexts'][] = 'url.query_args:' . MIRADOR_SEARCH_HIGHLIGHT_PARAM;
$term = \Drupal::request()->query->get(MIRADOR_SEARCH_HIGHLIGHT_PARAM, '');
$term = is_string($term) ? trim($term) : '';
if ($term === '') {
return;
}
$selector = '#' . $variables['mirador_view_id'];
$variables['#attached']['drupalSettings']['mirador']['viewers'][$selector]['windows'][0]['defaultSearchQuery'] = mb_substr($term, 0, 256);
}