diff --git a/src/CoreBundle/Controller/SearchController.php b/src/CoreBundle/Controller/SearchController.php
index 6e97c76c4be..cf94aa9c448 100644
--- a/src/CoreBundle/Controller/SearchController.php
+++ b/src/CoreBundle/Controller/SearchController.php
@@ -415,10 +415,19 @@ private function decorateResultsForUi(array $results, string $queryString): arra
$tool = (string) ($data['tool'] ?? '');
$isQuestion = ('question' === $kind) || ('quiz_question' === $tool);
- $title = isset($data['title']) ? (string) $data['title'] : '';
+ $title = isset($data['title']) ? $this->normalizeDisplayText((string) $data['title']) : '';
+ $data['title'] = $title;
$fullPath = isset($data['full_path']) ? (string) $data['full_path'] : '';
$content = isset($data['content']) ? (string) $data['content'] : '';
+ if (isset($data['title']) && \is_string($data['title'])) {
+ $data['title'] = html_entity_decode($data['title'], ENT_QUOTES | ENT_HTML5, 'UTF-8');
+ }
+
+ if (!empty($data['quiz_title']) && \is_string($data['quiz_title'])) {
+ $data['quiz_title'] = html_entity_decode($data['quiz_title'], ENT_QUOTES | ENT_HTML5, 'UTF-8');
+ }
+ $title = isset($data['title']) ? (string) $data['title'] : $title;
$ext = $this->guessFileExtension($fullPath, $title);
$data['file_ext'] = $ext;
$data['file_icon'] = $this->guessFileIconMdi($ext, (string) ($data['filetype'] ?? ''));
@@ -431,7 +440,7 @@ private function decorateResultsForUi(array $results, string $queryString): arra
if ($isQuestion) {
// Use quiz title as the main visible title if available.
if (!empty($data['quiz_title'])) {
- $data['title'] = (string) $data['quiz_title'];
+ $data['title'] = $this->normalizeDisplayText((string) $data['quiz_title']);
}
// Show only a tiny context around the match, never the full question content.
@@ -474,7 +483,7 @@ private function buildSafeQuestionExcerptHtml(string $content, array $terms, int
$matchedTerm = null;
foreach ($terms as $t) {
- $t = trim($t);
+ $t = trim((string) $t);
if ('' === $t) {
continue;
}
@@ -524,14 +533,14 @@ private function buildSafeQuestionExcerptHtml(string $content, array $terms, int
$escaped = htmlspecialchars($snippet, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
- // Highlight terms (small snippet, safe).
+ // Highlight terms
foreach ($terms as $t) {
- $t = trim($t);
+ $t = trim((string) $t);
if ('' === $t) {
continue;
}
$pattern = '/('.preg_quote(htmlspecialchars($t, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '/').')/iu';
- $escaped = preg_replace($pattern, '$1', $escaped) ?? $escaped;
+ $escaped = preg_replace($pattern, '$1', $escaped) ?? $escaped;
}
if ($start > 0) {
@@ -544,6 +553,18 @@ private function buildSafeQuestionExcerptHtml(string $content, array $terms, int
return $escaped;
}
+ /**
+ * Normalize a UI text field coming from the index.
+ * It may contain HTML entities (e.g. é) depending on how it was indexed.
+ */
+ private function normalizeDisplayText(string $text): string
+ {
+ $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
+ $text = preg_replace('/\s+/u', ' ', $text) ?? $text;
+
+ return trim($text);
+ }
+
/**
* @param array> $results
*/
@@ -842,14 +863,16 @@ private function buildExcerptHtml(string $content, array $terms, int $maxLen): s
}
$pos = null;
- $matchedTerm = null;
foreach ($terms as $t) {
+ $t = trim((string) $t);
+ if ('' === $t) {
+ continue;
+ }
+
$p = mb_stripos($plain, $t, 0, 'UTF-8');
if (false !== $p) {
$pos = (int) $p;
- $matchedTerm = $t;
-
break;
}
}
@@ -871,12 +894,12 @@ private function buildExcerptHtml(string $content, array $terms, int $maxLen): s
// Highlight all terms in escaped string.
foreach ($terms as $t) {
- $t = trim($t);
+ $t = trim((string) $t);
if ('' === $t) {
continue;
}
$pattern = '/('.preg_quote(htmlspecialchars($t, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '/').')/iu';
- $escaped = preg_replace($pattern, '$1', $escaped) ?? $escaped;
+ $escaped = preg_replace($pattern, '$1', $escaped) ?? $escaped;
}
if ($start > 0) {