2019-05-25 15:46:47 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Shaarli\Formatter;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class BookmarkDefaultFormatter
|
|
|
|
*
|
|
|
|
* Default bookmark formatter.
|
|
|
|
* Escape values for HTML display and automatically add link to URL and hashtags.
|
|
|
|
*
|
|
|
|
* @package Shaarli\Formatter
|
|
|
|
*/
|
|
|
|
class BookmarkDefaultFormatter extends BookmarkFormatter
|
|
|
|
{
|
2020-11-08 15:02:45 +01:00
|
|
|
protected const SEARCH_HIGHLIGHT_OPEN = '|@@HIGHLIGHT';
|
|
|
|
protected const SEARCH_HIGHLIGHT_CLOSE = 'HIGHLIGHT@@|';
|
2020-10-12 11:35:55 +02:00
|
|
|
|
2019-05-25 15:46:47 +02:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
2020-10-12 11:35:55 +02:00
|
|
|
protected function formatTitle($bookmark)
|
2019-05-25 15:46:47 +02:00
|
|
|
{
|
|
|
|
return escape($bookmark->getTitle());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
2020-10-12 11:35:55 +02:00
|
|
|
protected function formatTitleHtml($bookmark)
|
|
|
|
{
|
|
|
|
$title = $this->tokenizeSearchHighlightField(
|
|
|
|
$bookmark->getTitle() ?? '',
|
|
|
|
$bookmark->getAdditionalContentEntry('search_highlight')['title'] ?? []
|
|
|
|
);
|
|
|
|
|
|
|
|
return $this->replaceTokens(escape($title));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
protected function formatDescription($bookmark)
|
2019-05-25 15:46:47 +02:00
|
|
|
{
|
|
|
|
$indexUrl = ! empty($this->contextData['index_url']) ? $this->contextData['index_url'] : '';
|
2020-10-12 11:35:55 +02:00
|
|
|
$description = $this->tokenizeSearchHighlightField(
|
|
|
|
$bookmark->getDescription() ?? '',
|
|
|
|
$bookmark->getAdditionalContentEntry('search_highlight')['description'] ?? []
|
|
|
|
);
|
2020-11-03 12:38:38 +01:00
|
|
|
$description = format_description(
|
|
|
|
escape($description),
|
|
|
|
$indexUrl,
|
|
|
|
$this->conf->get('formatter_settings.autolink', true)
|
|
|
|
);
|
2020-10-12 11:35:55 +02:00
|
|
|
|
2020-11-03 12:38:38 +01:00
|
|
|
return $this->replaceTokens($description);
|
2019-05-25 15:46:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
protected function formatTagList($bookmark)
|
|
|
|
{
|
2020-01-18 11:33:23 +01:00
|
|
|
return escape(parent::formatTagList($bookmark));
|
2019-05-25 15:46:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
2020-10-12 11:35:55 +02:00
|
|
|
protected function formatTagListHtml($bookmark)
|
|
|
|
{
|
2020-10-22 16:21:03 +02:00
|
|
|
$tagsSeparator = $this->conf->get('general.tags_separator', ' ');
|
2020-10-12 11:35:55 +02:00
|
|
|
if (empty($bookmark->getAdditionalContentEntry('search_highlight')['tags'])) {
|
|
|
|
return $this->formatTagList($bookmark);
|
|
|
|
}
|
|
|
|
|
|
|
|
$tags = $this->tokenizeSearchHighlightField(
|
2020-10-22 16:21:03 +02:00
|
|
|
$bookmark->getTagsString($tagsSeparator),
|
2020-10-12 11:35:55 +02:00
|
|
|
$bookmark->getAdditionalContentEntry('search_highlight')['tags']
|
|
|
|
);
|
2020-10-22 16:21:03 +02:00
|
|
|
$tags = $this->filterTagList(tags_str2array($tags, $tagsSeparator));
|
2020-10-12 11:35:55 +02:00
|
|
|
$tags = escape($tags);
|
|
|
|
$tags = $this->replaceTokensArray($tags);
|
|
|
|
|
|
|
|
return $tags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
protected function formatTagString($bookmark)
|
2019-05-25 15:46:47 +02:00
|
|
|
{
|
2020-10-22 16:21:03 +02:00
|
|
|
return implode($this->conf->get('general.tags_separator'), $this->formatTagList($bookmark));
|
2019-05-25 15:46:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
2020-10-12 11:35:55 +02:00
|
|
|
protected function formatUrl($bookmark)
|
2019-05-25 15:46:47 +02:00
|
|
|
{
|
2020-07-28 21:09:22 +02:00
|
|
|
if ($bookmark->isNote() && isset($this->contextData['index_url'])) {
|
2020-07-28 20:46:11 +02:00
|
|
|
return rtrim($this->contextData['index_url'], '/') . '/' . escape(ltrim($bookmark->getUrl(), '/'));
|
2019-05-25 15:46:47 +02:00
|
|
|
}
|
2020-07-28 20:46:11 +02:00
|
|
|
|
2019-05-25 15:46:47 +02:00
|
|
|
return escape($bookmark->getUrl());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
protected function formatRealUrl($bookmark)
|
|
|
|
{
|
2020-07-28 20:46:11 +02:00
|
|
|
if ($bookmark->isNote()) {
|
2020-07-28 21:09:22 +02:00
|
|
|
if (isset($this->contextData['index_url'])) {
|
2020-07-28 20:46:11 +02:00
|
|
|
$prefix = rtrim($this->contextData['index_url'], '/') . '/';
|
|
|
|
}
|
|
|
|
|
2020-07-28 21:09:22 +02:00
|
|
|
if (isset($this->contextData['base_path'])) {
|
2020-07-28 20:46:11 +02:00
|
|
|
$prefix = rtrim($this->contextData['base_path'], '/') . '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
return escape($prefix ?? '') . escape(ltrim($bookmark->getUrl(), '/'));
|
2019-05-25 15:46:47 +02:00
|
|
|
}
|
2020-07-28 20:46:11 +02:00
|
|
|
|
2019-05-25 15:46:47 +02:00
|
|
|
return escape($bookmark->getUrl());
|
|
|
|
}
|
|
|
|
|
2020-10-12 11:35:55 +02:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
protected function formatUrlHtml($bookmark)
|
|
|
|
{
|
|
|
|
$url = $this->tokenizeSearchHighlightField(
|
|
|
|
$bookmark->getUrl() ?? '',
|
|
|
|
$bookmark->getAdditionalContentEntry('search_highlight')['url'] ?? []
|
|
|
|
);
|
|
|
|
|
|
|
|
return $this->replaceTokens(escape($url));
|
|
|
|
}
|
|
|
|
|
2019-05-25 15:46:47 +02:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
protected function formatThumbnail($bookmark)
|
|
|
|
{
|
|
|
|
return escape($bookmark->getThumbnail());
|
|
|
|
}
|
2020-10-12 11:35:55 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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],
|
|
|
|
['<span class="search-highlight">', '</span>'],
|
|
|
|
$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;
|
|
|
|
}
|
2019-05-25 15:46:47 +02:00
|
|
|
}
|