aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/formatter/BookmarkDefaultFormatter.php
diff options
context:
space:
mode:
Diffstat (limited to 'application/formatter/BookmarkDefaultFormatter.php')
-rw-r--r--application/formatter/BookmarkDefaultFormatter.php154
1 files changed, 141 insertions, 13 deletions
diff --git a/application/formatter/BookmarkDefaultFormatter.php b/application/formatter/BookmarkDefaultFormatter.php
index c6c59064..d58a5e39 100644
--- a/application/formatter/BookmarkDefaultFormatter.php
+++ b/application/formatter/BookmarkDefaultFormatter.php
@@ -12,10 +12,13 @@ namespace Shaarli\Formatter;
12 */ 12 */
13class BookmarkDefaultFormatter extends BookmarkFormatter 13class BookmarkDefaultFormatter extends BookmarkFormatter
14{ 14{
15 const SEARCH_HIGHLIGHT_OPEN = '|@@HIGHLIGHT';
16 const SEARCH_HIGHLIGHT_CLOSE = 'HIGHLIGHT@@|';
17
15 /** 18 /**
16 * @inheritdoc 19 * @inheritdoc
17 */ 20 */
18 public function formatTitle($bookmark) 21 protected function formatTitle($bookmark)
19 { 22 {
20 return escape($bookmark->getTitle()); 23 return escape($bookmark->getTitle());
21 } 24 }
@@ -23,10 +26,28 @@ class BookmarkDefaultFormatter extends BookmarkFormatter
23 /** 26 /**
24 * @inheritdoc 27 * @inheritdoc
25 */ 28 */
26 public function formatDescription($bookmark) 29 protected function formatTitleHtml($bookmark)
30 {
31 $title = $this->tokenizeSearchHighlightField(
32 $bookmark->getTitle() ?? '',
33 $bookmark->getAdditionalContentEntry('search_highlight')['title'] ?? []
34 );
35
36 return $this->replaceTokens(escape($title));
37 }
38
39 /**
40 * @inheritdoc
41 */
42 protected function formatDescription($bookmark)
27 { 43 {
28 $indexUrl = ! empty($this->contextData['index_url']) ? $this->contextData['index_url'] : ''; 44 $indexUrl = ! empty($this->contextData['index_url']) ? $this->contextData['index_url'] : '';
29 return format_description(escape($bookmark->getDescription()), $indexUrl); 45 $description = $this->tokenizeSearchHighlightField(
46 $bookmark->getDescription() ?? '',
47 $bookmark->getAdditionalContentEntry('search_highlight')['description'] ?? []
48 );
49
50 return $this->replaceTokens(format_description(escape($description), $indexUrl));
30 } 51 }
31 52
32 /** 53 /**
@@ -40,7 +61,27 @@ class BookmarkDefaultFormatter extends BookmarkFormatter
40 /** 61 /**
41 * @inheritdoc 62 * @inheritdoc
42 */ 63 */
43 public function formatTagString($bookmark) 64 protected function formatTagListHtml($bookmark)
65 {
66 if (empty($bookmark->getAdditionalContentEntry('search_highlight')['tags'])) {
67 return $this->formatTagList($bookmark);
68 }
69
70 $tags = $this->tokenizeSearchHighlightField(
71 $bookmark->getTagsString(),
72 $bookmark->getAdditionalContentEntry('search_highlight')['tags']
73 );
74 $tags = $this->filterTagList(explode(' ', $tags));
75 $tags = escape($tags);
76 $tags = $this->replaceTokensArray($tags);
77
78 return $tags;
79 }
80
81 /**
82 * @inheritdoc
83 */
84 protected function formatTagString($bookmark)
44 { 85 {
45 return implode(' ', $this->formatTagList($bookmark)); 86 return implode(' ', $this->formatTagList($bookmark));
46 } 87 }
@@ -48,13 +89,12 @@ class BookmarkDefaultFormatter extends BookmarkFormatter
48 /** 89 /**
49 * @inheritdoc 90 * @inheritdoc
50 */ 91 */
51 public function formatUrl($bookmark) 92 protected function formatUrl($bookmark)
52 { 93 {
53 if (! empty($this->contextData['index_url']) && ( 94 if ($bookmark->isNote() && isset($this->contextData['index_url'])) {
54 startsWith($bookmark->getUrl(), '?') || startsWith($bookmark->getUrl(), '/') 95 return rtrim($this->contextData['index_url'], '/') . '/' . escape(ltrim($bookmark->getUrl(), '/'));
55 )) {
56 return $this->contextData['index_url'] . escape($bookmark->getUrl());
57 } 96 }
97
58 return escape($bookmark->getUrl()); 98 return escape($bookmark->getUrl());
59 } 99 }
60 100
@@ -63,19 +103,107 @@ class BookmarkDefaultFormatter extends BookmarkFormatter
63 */ 103 */
64 protected function formatRealUrl($bookmark) 104 protected function formatRealUrl($bookmark)
65 { 105 {
66 if (! empty($this->contextData['index_url']) && ( 106 if ($bookmark->isNote()) {
67 startsWith($bookmark->getUrl(), '?') || startsWith($bookmark->getUrl(), '/') 107 if (isset($this->contextData['index_url'])) {
68 )) { 108 $prefix = rtrim($this->contextData['index_url'], '/') . '/';
69 return $this->contextData['index_url'] . escape($bookmark->getUrl()); 109 }
110
111 if (isset($this->contextData['base_path'])) {
112 $prefix = rtrim($this->contextData['base_path'], '/') . '/';
113 }
114
115 return escape($prefix ?? '') . escape(ltrim($bookmark->getUrl(), '/'));
70 } 116 }
117
71 return escape($bookmark->getUrl()); 118 return escape($bookmark->getUrl());
72 } 119 }
73 120
74 /** 121 /**
75 * @inheritdoc 122 * @inheritdoc
76 */ 123 */
124 protected function formatUrlHtml($bookmark)
125 {
126 $url = $this->tokenizeSearchHighlightField(
127 $bookmark->getUrl() ?? '',
128 $bookmark->getAdditionalContentEntry('search_highlight')['url'] ?? []
129 );
130
131 return $this->replaceTokens(escape($url));
132 }
133
134 /**
135 * @inheritdoc
136 */
77 protected function formatThumbnail($bookmark) 137 protected function formatThumbnail($bookmark)
78 { 138 {
79 return escape($bookmark->getThumbnail()); 139 return escape($bookmark->getThumbnail());
80 } 140 }
141
142 /**
143 * Insert search highlight token in provided field content based on a list of search result positions
144 *
145 * @param string $fieldContent
146 * @param array|null $positions List of of search results with 'start' and 'end' positions.
147 *
148 * @return string Updated $fieldContent.
149 */
150 protected function tokenizeSearchHighlightField(string $fieldContent, ?array $positions): string
151 {
152 if (empty($positions)) {
153 return $fieldContent;
154 }
155
156 $insertedTokens = 0;
157 $tokenLength = strlen(static::SEARCH_HIGHLIGHT_OPEN);
158 foreach ($positions as $position) {
159 $position = [
160 'start' => $position['start'] + ($insertedTokens * $tokenLength),
161 'end' => $position['end'] + ($insertedTokens * $tokenLength),
162 ];
163
164 $content = mb_substr($fieldContent, 0, $position['start']);
165 $content .= static::SEARCH_HIGHLIGHT_OPEN;
166 $content .= mb_substr($fieldContent, $position['start'], $position['end'] - $position['start']);
167 $content .= static::SEARCH_HIGHLIGHT_CLOSE;
168 $content .= mb_substr($fieldContent, $position['end']);
169
170 $fieldContent = $content;
171
172 $insertedTokens += 2;
173 }
174
175 return $fieldContent;
176 }
177
178 /**
179 * Replace search highlight tokens with HTML highlighted span.
180 *
181 * @param string $fieldContent
182 *
183 * @return string updated content.
184 */
185 protected function replaceTokens(string $fieldContent): string
186 {
187 return str_replace(
188 [static::SEARCH_HIGHLIGHT_OPEN, static::SEARCH_HIGHLIGHT_CLOSE],
189 ['<span class="search-highlight">', '</span>'],
190 $fieldContent
191 );
192 }
193
194 /**
195 * Apply replaceTokens to an array of content strings.
196 *
197 * @param string[] $fieldContents
198 *
199 * @return array
200 */
201 protected function replaceTokensArray(array $fieldContents): array
202 {
203 foreach ($fieldContents as &$entry) {
204 $entry = $this->replaceTokens($entry);
205 }
206
207 return $fieldContents;
208 }
81} 209}