Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,41 @@ public function execute($request)

$this->setView($request);

// Determine whether search highlighting is required
$hasQuery = 1 !== preg_match('/^[\s\t\r\n]*$/', $request->query) || 1 !== preg_match('/^[\s\t\r\n]*$/', $request->sq0);
$highlightSetting = QubitSetting::getByName('highlight_search_results');
$highlightValue = null === $highlightSetting ? 0 : intval($highlightSetting->getValue());
$highlightEnabled = 1 === $highlightValue;

// Add search term highlighting when any search criteria are present
if ($hasQuery && $highlightEnabled) {
$this->search->query->setHighlight([
'pre_tags' => ['<mark>'],
'post_tags' => ['</mark>'],
'fields' => [
// This wildcard captures highlighting for all fields. More specific configs
// are set below which override this wildcard
'*' => [
'number_of_fragments' => 1,
'fragment_size' => 150,
],
// The following fields may be rendered in results. Don't fragment in this case
'i18n.*.scopeAndContent' => [
'number_of_fragments' => 0,
'fragment_size' => 0,
],
'i18n.*.title' => [
'number_of_fragments' => 0,
'fragment_size' => 0,
],
'creators.i18n.*.authorizedFormOfName' => [
'number_of_fragments' => 0,
'fragment_size' => 0,
],
],
]);
}

$resultSet = QubitSearch::getInstance()->index->getIndex('QubitInformationObject')->search($this->search->getQuery(false, true));

// Page results
Expand Down
87 changes: 77 additions & 10 deletions apps/qubit/modules/search/templates/_searchResult.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
<?php $doc = $hit->getData(); ?>
<?php
$doc = $hit->getData();

// The highlights contain a mapping from search index field name to an array of fragments
// containing the parts of that field that matched the query in the search index. If a field did
// not return results for the query, or higlighting is disabled, the key for that field will not
// exist in this array.
$highlights = reset($hit->getHighlights());
$titleHighlight = $highlights["i18n.{$culture}.title"][0] ?? null;
$scopeHighlight = $highlights["i18n.{$culture}.scopeAndContent"][0] ?? null;
$creatorHighlight = $highlights["creators.i18n.{$culture}.authorizedFormOfName"][0] ?? null;
$refCodeHighlight = $highlights['referenceCode'][0] ?? null;
$identifierHighlight = $highlights['identifier'][0] ?? null;

// We can render other highlights, but we want to ensure that they're not already rendered in the
// search result.
$highlightsRenderedElsewhere = [
"i18n.{$culture}.title",
"i18n.{$culture}.scopeAndContent",
"creators.i18n.{$culture}.authorizedFormOfName",
'referenceCode',
'identifier',
];

$otherHighlights = array_diff_key($highlights, array_flip($highlightsRenderedElsewhere));

$maxFragmentSize = 150;
?>

<article class="search-result row g-0 p-3 border-bottom">
<?php if (!empty($doc['hasDigitalObject'])) { ?>
Expand Down Expand Up @@ -39,10 +66,10 @@
<div class="col-12<?php echo empty($doc['hasDigitalObject']) ? '' : ' col-lg-9'; ?> d-flex flex-column gap-1">
<div class="d-flex align-items-center gap-2">
<?php echo link_to(
render_title(get_search_i18n(
render_title_with_highlights(get_search_i18n(
$doc,
'title',
['allowEmpty' => false, 'culture' => $culture]
['allowEmpty' => false, 'culture' => $culture, 'highlight' => $titleHighlight],
)),
['module' => 'informationobject', 'slug' => $doc['slug']],
['class' => 'h5 mb-0 text-truncate'],
Expand All @@ -63,10 +90,20 @@
'1' == sfConfig::get('app_inherit_code_informationobject', 1)
&& isset($doc['referenceCode']) && !empty($doc['referenceCode'])
) { ?>
<span class="text-primary"><?php echo $doc['referenceCode']; ?></span>
<span class="text-primary">
<?php
$refCode = null !== $refCodeHighlight ? render_value_with_highlights($refCodeHighlight) : $doc['referenceCode'];
echo $refCode;
?>
</span>
<?php $showDash = true; ?>
<?php } elseif (isset($doc['identifier']) && !empty($doc['identifier'])) { ?>
<span class="text-primary"><?php echo $doc['identifier']; ?></span>
<span class="text-primary">
<?php
$identifier = null !== $identifierHighlight ? render_value_with_highlights($identifierHighlight) : $doc['identifier'];
echo $identifier;
?>
</span>
<?php $showDash = true; ?>
<?php } ?>

Expand Down Expand Up @@ -124,28 +161,58 @@
)),
['slug' => $doc['partOf']['slug'], 'module' => 'informationobject']
); ?>
</span>
</span>
<?php } ?>
</div>

<?php if (null !== $scopeAndContent = get_search_i18n(
$doc,
'scopeAndContent',
['culture' => $culture]
['culture' => $culture, 'highlight' => $scopeHighlight],
)) { ?>
<span class="text-block d-none">
<?php echo render_value($scopeAndContent); ?>
<?php echo render_value_with_highlights($scopeAndContent); ?>
</span>
<?php } ?>

<?php if (
isset($doc['creators'])
&& null !== $creationDetails = get_search_creation_details($doc, $culture)
&& null !== $creationDetails = get_search_creation_details($doc, ['allowEmpty' => false, 'culture' => $culture, 'cultureFallback' => true, 'highlight' => $creatorHighlight])
) { ?>
<span class="text-muted">
<?php echo render_value_inline($creationDetails); ?>
<?php echo render_value_with_highlights($creationDetails); ?>
</span>
<?php } ?>

<?php if (!empty($otherHighlights)) { ?>
<?php
$firstHighlightText = current($otherHighlights)[0];
$highlightFieldKey = array_key_first($otherHighlights);
$ellipsize = strlen($firstHighlightText) >= $maxFragmentSize;
?>
<div class="search-highlight-other d-print-none">
<div class="text-block highlight-summary summary">
<span>
<i class="fas fa-search" aria-hidden="true"></i>
&nbsp;
<?php if ('transcript' === $highlightFieldKey) {
echo __('Search matched digital object transcript:');
} else {
echo __('Search matched:');
} ?>
</span>
<span class="search-highlight-fragment">
<?php if ($ellipsize) {
echo '&hellip;';
} ?>
<?php echo render_value_with_highlights($firstHighlightText); ?>
<?php if ($ellipsize) {
echo '&hellip;';
} ?>
</span>
</div>
</div>
<?php } ?>
</div>
</div>
</article>
11 changes: 11 additions & 0 deletions apps/qubit/modules/settings/actions/globalAction.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ protected function populateGlobalForm()
$multiRepository = QubitSetting::getByName('multi_repository');
$auditLogEnabled = QubitSetting::getByName('audit_log_enabled');
$showTooltips = QubitSetting::getByName('show_tooltips');
$highlightSearchResults = QubitSetting::getByName('highlight_search_results');
$defaultPubStatus = QubitSetting::getByName('defaultPubStatus');
$draftNotificationEnabled = QubitSetting::getByName('draft_notification_enabled');
$swordDepositDir = QubitSetting::getByName('sword_deposit_dir');
Expand All @@ -103,6 +104,7 @@ protected function populateGlobalForm()
'slug_basis_informationobject' => (isset($slugTypeInformationObject)) ? intval($slugTypeInformationObject->getValue(['sourceCulture' => true])) : QubitSlug::SLUG_BASIS_TITLE,
'permissive_slug_creation' => (isset($permissiveSlugCreation)) ? intval($permissiveSlugCreation->getValue(['sourceCulture' => true])) : QubitSlug::SLUG_RESTRICTIVE,
'show_tooltips' => (isset($showTooltips)) ? intval($showTooltips->getValue(['sourceCulture' => true])) : 1,
'highlight_search_results' => (isset($highlightSearchResults)) ? intval($highlightSearchResults->getValue(['sourceCulture' => true])) : 1,
'defaultPubStatus' => (isset($defaultPubStatus)) ? $defaultPubStatus->getValue(['sourceCulture' => true]) : QubitTerm::PUBLICATION_STATUS_DRAFT_ID,
'draft_notification_enabled' => (isset($draftNotificationEnabled)) ? intval($draftNotificationEnabled->getValue(['sourceCulture' => true])) : 0,
'sword_deposit_dir' => (isset($swordDepositDir)) ? $swordDepositDir->getValue(['sourceCulture' => true]) : null,
Expand Down Expand Up @@ -243,6 +245,15 @@ protected function updateGlobalSettings()
$setting->save();
}

// Highlight search results
if (null !== $highlightSearchResults = $thisForm->getValue('highlight_search_results')) {
$setting = QubitSetting::getByName('highlight_search_results');

// Force sourceCulture update to prevent discrepency in settings between cultures
$setting->setValue($highlightSearchResults, ['sourceCulture' => true]);
$setting->save();
}

// Default publication status
if (null !== $defaultPubStatus = $thisForm->getValue('defaultPubStatus')) {
$setting = QubitSetting::getByName('defaultPubStatus');
Expand Down
2 changes: 2 additions & 0 deletions apps/qubit/modules/settings/templates/globalSuccess.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
<?php echo render_field($globalForm->default_repository_browse_view); ?>

<?php echo render_field($globalForm->escape_queries); ?>

<?php echo render_field($globalForm->highlight_search_results); ?>
</div>
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions data/fixtures/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,9 @@ QubitSetting:
QubitSetting_showTooltips:
name: show_tooltips
value: 1
QubitSetting_highlightSearchResults:
name: highlight_search_results
value: 1
QubitSetting_accessionMask:
name: accession_mask
value: '%Y-%m-%d/#i'
Expand Down
4 changes: 4 additions & 0 deletions lib/form/SettingsGlobalForm.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function configure()
'enable_institutional_scoping' => new sfWidgetFormSelectRadio(['choices' => $options], ['class' => 'radio']),
'audit_log_enabled' => new sfWidgetFormSelectRadio(['choices' => $options], ['class' => 'radio']),
'show_tooltips' => new sfWidgetFormSelectRadio(['choices' => $options], ['class' => 'radio']),
'highlight_search_results' => new sfWidgetFormSelectRadio(['choices' => $options], ['class' => 'radio']),
'slug_basis_informationobject' => $this->getSlugBasisInformationObjectWidget(),
'permissive_slug_creation' => new sfWidgetFormSelectRadio(['choices' => [QubitSlug::SLUG_PERMISSIVE => $this->i18n->__('Yes'), QubitSlug::SLUG_RESTRICTIVE => $this->i18n->__('No')]], ['class' => 'radio']),
'defaultPubStatus' => new sfWidgetFormSelectRadio(['choices' => [QubitTerm::PUBLICATION_STATUS_DRAFT_ID => $this->i18n->__('Draft'), QubitTerm::PUBLICATION_STATUS_PUBLISHED_ID => $this->i18n->__('Published')]], ['class' => 'radio']),
Expand All @@ -70,6 +71,7 @@ public function configure()
'enable_institutional_scoping' => $this->i18n->__('Enable institutional scoping'),
'audit_log_enabled' => $this->i18n->__('Enable description change logging'),
'show_tooltips' => $this->i18n->__('Show tooltips'),
'highlight_search_results' => $this->i18n->__('Highlight search results'),
'defaultPubStatus' => $this->i18n->__('Default publication status'),
'draft_notification_enabled' => $this->i18n->__('Show available drafts notification upon user login'),
'sword_deposit_dir' => $this->i18n->__('SWORD deposit directory'),
Expand All @@ -92,6 +94,7 @@ public function configure()
'separator_character' => $this->i18n->__('The character separating hierarchical elements in a reference code'),
'inherit_code_informationobject' => $this->i18n->__('When set to &quot;yes&quot;, the reference code string will be built using the information object identifier plus the identifiers of all its ancestors'),
'escape_queries' => $this->i18n->__('A list of special chars, separated by coma, to be escaped in string queries'),
'highlight_search_results' => $this->i18n->__('Visually highlight how the search criteria matches the search results'),
'multi_repository' => $this->i18n->__('When set to &quot;no&quot;, the repository name is excluded from certain displays because it will be too repetitive'),
'enable_institutional_scoping' => $this->i18n->__('Applies to multi-repository sites only. When set to &quot;yes&quot;, additional search and browse options will be available at the repository level'),
'defaultPubStatus' => $this->i18n->__('Default publication status for newly created or imported %1%', ['%1%' => sfConfig::get('app_ui_label_informationobject')]),
Expand Down Expand Up @@ -129,6 +132,7 @@ public function configure()
$this->validatorSchema['permissive_slug_creation'] = new sfValidatorInteger(['required' => false]);
$this->validatorSchema['audit_log_enabled'] = new sfValidatorInteger(['required' => false]);
$this->validatorSchema['show_tooltips'] = new sfValidatorInteger(['required' => false]);
$this->validatorSchema['highlight_search_results'] = new sfValidatorInteger(['required' => false]);
$this->validatorSchema['defaultPubStatus'] = new sfValidatorChoice(['choices' => [QubitTerm::PUBLICATION_STATUS_DRAFT_ID, QubitTerm::PUBLICATION_STATUS_PUBLISHED_ID]]);
$this->validatorSchema['draft_notification_enabled'] = new sfValidatorInteger(['required' => false]);
$this->validatorSchema['sword_deposit_dir'] = new sfValidatorString(['required' => false]);
Expand Down
Loading
Loading