]> git.immae.eu Git - github/shaarli/Shaarli.git/blame_incremental - application/formatter/BookmarkDefaultFormatter.php
ShaarliParsedown: add PHPDoc/comments
[github/shaarli/Shaarli.git] / application / formatter / BookmarkDefaultFormatter.php
... / ...
CommitLineData
1<?php
2
3namespace Shaarli\Formatter;
4
5/**
6 * Class BookmarkDefaultFormatter
7 *
8 * Default bookmark formatter.
9 * Escape values for HTML display and automatically add link to URL and hashtags.
10 *
11 * @package Shaarli\Formatter
12 */
13class BookmarkDefaultFormatter extends BookmarkFormatter
14{
15 public const SEARCH_HIGHLIGHT_OPEN = '||O_HIGHLIGHT';
16 public const SEARCH_HIGHLIGHT_CLOSE = '||C_HIGHLIGHT';
17
18 /**
19 * @inheritdoc
20 */
21 protected function formatTitle($bookmark)
22 {
23 return escape($bookmark->getTitle());
24 }
25
26 /**
27 * @inheritdoc
28 */
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)
43 {
44 $indexUrl = ! empty($this->contextData['index_url']) ? $this->contextData['index_url'] : '';
45 $description = $this->tokenizeSearchHighlightField(
46 $bookmark->getDescription() ?? '',
47 $bookmark->getAdditionalContentEntry('search_highlight')['description'] ?? []
48 );
49 $description = format_description(
50 escape($description),
51 $indexUrl,
52 $this->conf->get('formatter_settings.autolink', true)
53 );
54
55 return $this->replaceTokens($description);
56 }
57
58 /**
59 * @inheritdoc
60 */
61 protected function formatTagList($bookmark)
62 {
63 return escape(parent::formatTagList($bookmark));
64 }
65
66 /**
67 * @inheritdoc
68 */
69 protected function formatTagListHtml($bookmark)
70 {
71 $tagsSeparator = $this->conf->get('general.tags_separator', ' ');
72 if (empty($bookmark->getAdditionalContentEntry('search_highlight')['tags'])) {
73 return $this->formatTagList($bookmark);
74 }
75
76 $tags = $this->tokenizeSearchHighlightField(
77 $bookmark->getTagsString($tagsSeparator),
78 $bookmark->getAdditionalContentEntry('search_highlight')['tags']
79 );
80 $tags = $this->filterTagList(tags_str2array($tags, $tagsSeparator));
81 $tags = escape($tags);
82 $tags = $this->replaceTokensArray($tags);
83
84 return $tags;
85 }
86
87 /**
88 * @inheritdoc
89 */
90 protected function formatTagString($bookmark)
91 {
92 return implode($this->conf->get('general.tags_separator'), $this->formatTagList($bookmark));
93 }
94
95 /**
96 * @inheritdoc
97 */
98 protected function formatUrl($bookmark)
99 {
100 if ($bookmark->isNote() && isset($this->contextData['index_url'])) {
101 return rtrim($this->contextData['index_url'], '/') . '/' . escape(ltrim($bookmark->getUrl(), '/'));
102 }
103
104 return escape($bookmark->getUrl());
105 }
106
107 /**
108 * @inheritdoc
109 */
110 protected function formatRealUrl($bookmark)
111 {
112 if ($bookmark->isNote()) {
113 if (isset($this->contextData['index_url'])) {
114 $prefix = rtrim($this->contextData['index_url'], '/') . '/';
115 }
116
117 if (isset($this->contextData['base_path'])) {
118 $prefix = rtrim($this->contextData['base_path'], '/') . '/';
119 }
120
121 return escape($prefix ?? '') . escape(ltrim($bookmark->getUrl(), '/'));
122 }
123
124 return escape($bookmark->getUrl());
125 }
126
127 /**
128 * @inheritdoc
129 */
130 protected function formatUrlHtml($bookmark)
131 {
132 $url = $this->tokenizeSearchHighlightField(
133 $bookmark->getUrl() ?? '',
134 $bookmark->getAdditionalContentEntry('search_highlight')['url'] ?? []
135 );
136
137 return $this->replaceTokens(escape($url));
138 }
139
140 /**
141 * @inheritdoc
142 */
143 protected function formatThumbnail($bookmark)
144 {
145 return escape($bookmark->getThumbnail());
146 }
147
148 /**
149 * Insert search highlight token in provided field content based on a list of search result positions
150 *
151 * @param string $fieldContent
152 * @param array|null $positions List of of search results with 'start' and 'end' positions.
153 *
154 * @return string Updated $fieldContent.
155 */
156 protected function tokenizeSearchHighlightField(string $fieldContent, ?array $positions): string
157 {
158 if (empty($positions)) {
159 return $fieldContent;
160 }
161
162 $insertedTokens = 0;
163 $tokenLength = strlen(static::SEARCH_HIGHLIGHT_OPEN);
164 foreach ($positions as $position) {
165 $position = [
166 'start' => $position['start'] + ($insertedTokens * $tokenLength),
167 'end' => $position['end'] + ($insertedTokens * $tokenLength),
168 ];
169
170 $content = mb_substr($fieldContent, 0, $position['start']);
171 $content .= static::SEARCH_HIGHLIGHT_OPEN;
172 $content .= mb_substr($fieldContent, $position['start'], $position['end'] - $position['start']);
173 $content .= static::SEARCH_HIGHLIGHT_CLOSE;
174 $content .= mb_substr($fieldContent, $position['end']);
175
176 $fieldContent = $content;
177
178 $insertedTokens += 2;
179 }
180
181 return $fieldContent;
182 }
183
184 /**
185 * Replace search highlight tokens with HTML highlighted span.
186 *
187 * @param string $fieldContent
188 *
189 * @return string updated content.
190 */
191 protected function replaceTokens(string $fieldContent): string
192 {
193 return str_replace(
194 [static::SEARCH_HIGHLIGHT_OPEN, static::SEARCH_HIGHLIGHT_CLOSE],
195 ['<span class="search-highlight">', '</span>'],
196 $fieldContent
197 );
198 }
199
200 /**
201 * Apply replaceTokens to an array of content strings.
202 *
203 * @param string[] $fieldContents
204 *
205 * @return array
206 */
207 protected function replaceTokensArray(array $fieldContents): array
208 {
209 foreach ($fieldContents as &$entry) {
210 $entry = $this->replaceTokens($entry);
211 }
212
213 return $fieldContents;
214 }
215}