X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=application%2Fformatter%2FBookmarkDefaultFormatter.php;h=7e93bf717aef98d6fecdb7d70cac0f015bf02a15;hb=9ef8555ad298668bcb8537ccdd2ab6560f44177f;hp=9d4a0fa0235c591be9a29d7ad3714d15b97c1e49;hpb=af41d5ab5d2bd3ba64d052c997bc6afa6966a63c;p=github%2Fshaarli%2FShaarli.git diff --git a/application/formatter/BookmarkDefaultFormatter.php b/application/formatter/BookmarkDefaultFormatter.php index 9d4a0fa0..7e93bf71 100644 --- a/application/formatter/BookmarkDefaultFormatter.php +++ b/application/formatter/BookmarkDefaultFormatter.php @@ -12,10 +12,13 @@ namespace Shaarli\Formatter; */ class BookmarkDefaultFormatter extends BookmarkFormatter { + public const SEARCH_HIGHLIGHT_OPEN = '||O_HIGHLIGHT'; + public const SEARCH_HIGHLIGHT_CLOSE = '||C_HIGHLIGHT'; + /** * @inheritdoc */ - public function formatTitle($bookmark) + protected function formatTitle($bookmark) { return escape($bookmark->getTitle()); } @@ -23,10 +26,33 @@ class BookmarkDefaultFormatter extends BookmarkFormatter /** * @inheritdoc */ - public function formatDescription($bookmark) + protected function formatTitleHtml($bookmark) + { + $title = $this->tokenizeSearchHighlightField( + $bookmark->getTitle() ?? '', + $bookmark->getAdditionalContentEntry('search_highlight')['title'] ?? [] + ); + + return $this->replaceTokens(escape($title)); + } + + /** + * @inheritdoc + */ + protected function formatDescription($bookmark) { $indexUrl = ! empty($this->contextData['index_url']) ? $this->contextData['index_url'] : ''; - return format_description(escape($bookmark->getDescription()), $indexUrl); + $description = $this->tokenizeSearchHighlightField( + $bookmark->getDescription() ?? '', + $bookmark->getAdditionalContentEntry('search_highlight')['description'] ?? [] + ); + $description = format_description( + escape($description), + $indexUrl, + $this->conf->get('formatter_settings.autolink', true) + ); + + return $this->replaceTokens($description); } /** @@ -40,15 +66,36 @@ class BookmarkDefaultFormatter extends BookmarkFormatter /** * @inheritdoc */ - public function formatTagString($bookmark) + protected function formatTagListHtml($bookmark) { - return implode(' ', $this->formatTagList($bookmark)); + $tagsSeparator = $this->conf->get('general.tags_separator', ' '); + if (empty($bookmark->getAdditionalContentEntry('search_highlight')['tags'])) { + return $this->formatTagList($bookmark); + } + + $tags = $this->tokenizeSearchHighlightField( + $bookmark->getTagsString($tagsSeparator), + $bookmark->getAdditionalContentEntry('search_highlight')['tags'] + ); + $tags = $this->filterTagList(tags_str2array($tags, $tagsSeparator)); + $tags = escape($tags); + $tags = $this->replaceTokensArray($tags); + + return $tags; } /** * @inheritdoc */ - public function formatUrl($bookmark) + protected function formatTagString($bookmark) + { + return implode($this->conf->get('general.tags_separator'), $this->formatTagList($bookmark)); + } + + /** + * @inheritdoc + */ + protected function formatUrl($bookmark) { if ($bookmark->isNote() && isset($this->contextData['index_url'])) { return rtrim($this->contextData['index_url'], '/') . '/' . escape(ltrim($bookmark->getUrl(), '/')); @@ -77,6 +124,19 @@ class BookmarkDefaultFormatter extends BookmarkFormatter return escape($bookmark->getUrl()); } + /** + * @inheritdoc + */ + protected function formatUrlHtml($bookmark) + { + $url = $this->tokenizeSearchHighlightField( + $bookmark->getUrl() ?? '', + $bookmark->getAdditionalContentEntry('search_highlight')['url'] ?? [] + ); + + return $this->replaceTokens(escape($url)); + } + /** * @inheritdoc */ @@ -84,4 +144,72 @@ class BookmarkDefaultFormatter extends BookmarkFormatter { return escape($bookmark->getThumbnail()); } + + /** + * Insert search highlight token in provided field content based on a list of search result positions + * + * @param string $fieldContent + * @param array|null $positions List of of search results with 'start' and 'end' positions. + * + * @return string Updated $fieldContent. + */ + protected function tokenizeSearchHighlightField(string $fieldContent, ?array $positions): string + { + if (empty($positions)) { + return $fieldContent; + } + + $insertedTokens = 0; + $tokenLength = strlen(static::SEARCH_HIGHLIGHT_OPEN); + foreach ($positions as $position) { + $position = [ + 'start' => $position['start'] + ($insertedTokens * $tokenLength), + 'end' => $position['end'] + ($insertedTokens * $tokenLength), + ]; + + $content = mb_substr($fieldContent, 0, $position['start']); + $content .= static::SEARCH_HIGHLIGHT_OPEN; + $content .= mb_substr($fieldContent, $position['start'], $position['end'] - $position['start']); + $content .= static::SEARCH_HIGHLIGHT_CLOSE; + $content .= mb_substr($fieldContent, $position['end']); + + $fieldContent = $content; + + $insertedTokens += 2; + } + + return $fieldContent; + } + + /** + * Replace search highlight tokens with HTML highlighted span. + * + * @param string $fieldContent + * + * @return string updated content. + */ + protected function replaceTokens(string $fieldContent): string + { + return str_replace( + [static::SEARCH_HIGHLIGHT_OPEN, static::SEARCH_HIGHLIGHT_CLOSE], + ['', ''], + $fieldContent + ); + } + + /** + * Apply replaceTokens to an array of content strings. + * + * @param string[] $fieldContents + * + * @return array + */ + protected function replaceTokensArray(array $fieldContents): array + { + foreach ($fieldContents as &$entry) { + $entry = $this->replaceTokens($entry); + } + + return $fieldContents; + } }