aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/feed/FeedBuilder.php
diff options
context:
space:
mode:
Diffstat (limited to 'application/feed/FeedBuilder.php')
-rw-r--r--application/feed/FeedBuilder.php189
1 files changed, 91 insertions, 98 deletions
diff --git a/application/feed/FeedBuilder.php b/application/feed/FeedBuilder.php
index 7c859474..f6def630 100644
--- a/application/feed/FeedBuilder.php
+++ b/application/feed/FeedBuilder.php
@@ -2,6 +2,9 @@
2namespace Shaarli\Feed; 2namespace Shaarli\Feed;
3 3
4use DateTime; 4use DateTime;
5use Shaarli\Bookmark\Bookmark;
6use Shaarli\Bookmark\BookmarkServiceInterface;
7use Shaarli\Formatter\BookmarkFormatter;
5 8
6/** 9/**
7 * FeedBuilder class. 10 * FeedBuilder class.
@@ -26,37 +29,30 @@ class FeedBuilder
26 public static $DEFAULT_LANGUAGE = 'en-en'; 29 public static $DEFAULT_LANGUAGE = 'en-en';
27 30
28 /** 31 /**
29 * @var int Number of links to display in a feed by default. 32 * @var int Number of bookmarks to display in a feed by default.
30 */ 33 */
31 public static $DEFAULT_NB_LINKS = 50; 34 public static $DEFAULT_NB_LINKS = 50;
32 35
33 /** 36 /**
34 * @var \Shaarli\Bookmark\LinkDB instance. 37 * @var BookmarkServiceInterface instance.
35 */ 38 */
36 protected $linkDB; 39 protected $linkDB;
37 40
38 /** 41 /**
39 * @var string RSS or ATOM feed. 42 * @var BookmarkFormatter instance.
40 */ 43 */
41 protected $feedType; 44 protected $formatter;
42 45
43 /** 46 /** @var mixed[] $_SERVER */
44 * @var array $_SERVER
45 */
46 protected $serverInfo; 47 protected $serverInfo;
47 48
48 /** 49 /**
49 * @var array $_GET
50 */
51 protected $userInput;
52
53 /**
54 * @var boolean True if the user is currently logged in, false otherwise. 50 * @var boolean True if the user is currently logged in, false otherwise.
55 */ 51 */
56 protected $isLoggedIn; 52 protected $isLoggedIn;
57 53
58 /** 54 /**
59 * @var boolean Use permalinks instead of direct links if true. 55 * @var boolean Use permalinks instead of direct bookmarks if true.
60 */ 56 */
61 protected $usePermalinks; 57 protected $usePermalinks;
62 58
@@ -69,7 +65,6 @@ class FeedBuilder
69 * @var string server locale. 65 * @var string server locale.
70 */ 66 */
71 protected $locale; 67 protected $locale;
72
73 /** 68 /**
74 * @var DateTime Latest item date. 69 * @var DateTime Latest item date.
75 */ 70 */
@@ -78,38 +73,38 @@ class FeedBuilder
78 /** 73 /**
79 * Feed constructor. 74 * Feed constructor.
80 * 75 *
81 * @param \Shaarli\Bookmark\LinkDB $linkDB LinkDB instance. 76 * @param BookmarkServiceInterface $linkDB LinkDB instance.
82 * @param string $feedType Type of feed. 77 * @param BookmarkFormatter $formatter instance.
83 * @param array $serverInfo $_SERVER. 78 * @param array $serverInfo $_SERVER.
84 * @param array $userInput $_GET. 79 * @param boolean $isLoggedIn True if the user is currently logged in, false otherwise.
85 * @param boolean $isLoggedIn True if the user is currently logged in,
86 * false otherwise.
87 */ 80 */
88 public function __construct($linkDB, $feedType, $serverInfo, $userInput, $isLoggedIn) 81 public function __construct($linkDB, $formatter, $serverInfo, $isLoggedIn)
89 { 82 {
90 $this->linkDB = $linkDB; 83 $this->linkDB = $linkDB;
91 $this->feedType = $feedType; 84 $this->formatter = $formatter;
92 $this->serverInfo = $serverInfo; 85 $this->serverInfo = $serverInfo;
93 $this->userInput = $userInput;
94 $this->isLoggedIn = $isLoggedIn; 86 $this->isLoggedIn = $isLoggedIn;
95 } 87 }
96 88
97 /** 89 /**
98 * Build data for feed templates. 90 * Build data for feed templates.
99 * 91 *
92 * @param string $feedType Type of feed (RSS/ATOM).
93 * @param array $userInput $_GET.
94 *
100 * @return array Formatted data for feeds templates. 95 * @return array Formatted data for feeds templates.
101 */ 96 */
102 public function buildData() 97 public function buildData(string $feedType, ?array $userInput)
103 { 98 {
104 // Search for untagged links 99 // Search for untagged bookmarks
105 if (isset($this->userInput['searchtags']) && empty($this->userInput['searchtags'])) { 100 if (isset($this->userInput['searchtags']) && empty($userInput['searchtags'])) {
106 $this->userInput['searchtags'] = false; 101 $userInput['searchtags'] = false;
107 } 102 }
108 103
109 // Optionally filter the results: 104 // Optionally filter the results:
110 $linksToDisplay = $this->linkDB->filterSearch($this->userInput); 105 $linksToDisplay = $this->linkDB->search($userInput, null, false, false, true);
111 106
112 $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay)); 107 $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay), $userInput);
113 108
114 // Can't use array_keys() because $link is a LinkDB instance and not a real array. 109 // Can't use array_keys() because $link is a LinkDB instance and not a real array.
115 $keys = array(); 110 $keys = array();
@@ -118,17 +113,18 @@ class FeedBuilder
118 } 113 }
119 114
120 $pageaddr = escape(index_url($this->serverInfo)); 115 $pageaddr = escape(index_url($this->serverInfo));
116 $this->formatter->addContextData('index_url', $pageaddr);
121 $linkDisplayed = array(); 117 $linkDisplayed = array();
122 for ($i = 0; $i < $nblinksToDisplay && $i < count($keys); $i++) { 118 for ($i = 0; $i < $nblinksToDisplay && $i < count($keys); $i++) {
123 $linkDisplayed[$keys[$i]] = $this->buildItem($linksToDisplay[$keys[$i]], $pageaddr); 119 $linkDisplayed[$keys[$i]] = $this->buildItem($feedType, $linksToDisplay[$keys[$i]], $pageaddr);
124 } 120 }
125 121
126 $data['language'] = $this->getTypeLanguage(); 122 $data['language'] = $this->getTypeLanguage($feedType);
127 $data['last_update'] = $this->getLatestDateFormatted(); 123 $data['last_update'] = $this->getLatestDateFormatted($feedType);
128 $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; 124 $data['show_dates'] = !$this->hideDates || $this->isLoggedIn;
129 // Remove leading slash from REQUEST_URI. 125 // Remove leading path from REQUEST_URI (already contained in $pageaddr).
130 $data['self_link'] = escape(server_url($this->serverInfo)) 126 $requestUri = preg_replace('#(.*?/)(feed.*)#', '$2', escape($this->serverInfo['REQUEST_URI']));
131 . escape($this->serverInfo['REQUEST_URI']); 127 $data['self_link'] = $pageaddr . $requestUri;
132 $data['index_url'] = $pageaddr; 128 $data['index_url'] = $pageaddr;
133 $data['usepermalinks'] = $this->usePermalinks === true; 129 $data['usepermalinks'] = $this->usePermalinks === true;
134 $data['links'] = $linkDisplayed; 130 $data['links'] = $linkDisplayed;
@@ -137,56 +133,7 @@ class FeedBuilder
137 } 133 }
138 134
139 /** 135 /**
140 * Build a feed item (one per shaare). 136 * Set this to true to use permalinks instead of direct bookmarks.
141 *
142 * @param array $link Single link array extracted from LinkDB.
143 * @param string $pageaddr Index URL.
144 *
145 * @return array Link array with feed attributes.
146 */
147 protected function buildItem($link, $pageaddr)
148 {
149 $link['guid'] = $pageaddr . '?' . $link['shorturl'];
150 // Prepend the root URL for notes
151 if (is_note($link['url'])) {
152 $link['url'] = $pageaddr . $link['url'];
153 }
154 if ($this->usePermalinks === true) {
155 $permalink = '<a href="' . $link['url'] . '" title="' . t('Direct link') . '">' . t('Direct link') . '</a>';
156 } else {
157 $permalink = '<a href="' . $link['guid'] . '" title="' . t('Permalink') . '">' . t('Permalink') . '</a>';
158 }
159 $link['description'] = format_description($link['description'], $pageaddr);
160 $link['description'] .= PHP_EOL . '<br>&#8212; ' . $permalink;
161
162 $pubDate = $link['created'];
163 $link['pub_iso_date'] = $this->getIsoDate($pubDate);
164
165 // atom:entry elements MUST contain exactly one atom:updated element.
166 if (!empty($link['updated'])) {
167 $upDate = $link['updated'];
168 $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM);
169 } else {
170 $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);
171 }
172
173 // Save the more recent item.
174 if (empty($this->latestDate) || $this->latestDate < $pubDate) {
175 $this->latestDate = $pubDate;
176 }
177 if (!empty($upDate) && $this->latestDate < $upDate) {
178 $this->latestDate = $upDate;
179 }
180
181 $taglist = array_filter(explode(' ', $link['tags']), 'strlen');
182 uasort($taglist, 'strcasecmp');
183 $link['taglist'] = $taglist;
184
185 return $link;
186 }
187
188 /**
189 * Set this to true to use permalinks instead of direct links.
190 * 137 *
191 * @param boolean $usePermalinks true to force permalinks. 138 * @param boolean $usePermalinks true to force permalinks.
192 */ 139 */
@@ -216,21 +163,63 @@ class FeedBuilder
216 } 163 }
217 164
218 /** 165 /**
166 * Build a feed item (one per shaare).
167 *
168 * @param string $feedType Type of feed (RSS/ATOM).
169 * @param Bookmark $link Single link array extracted from LinkDB.
170 * @param string $pageaddr Index URL.
171 *
172 * @return array Link array with feed attributes.
173 */
174 protected function buildItem(string $feedType, $link, $pageaddr)
175 {
176 $data = $this->formatter->format($link);
177 $data['guid'] = rtrim($pageaddr, '/') . '/shaare/' . $data['shorturl'];
178 if ($this->usePermalinks === true) {
179 $permalink = '<a href="'. $data['url'] .'" title="'. t('Direct link') .'">'. t('Direct link') .'</a>';
180 } else {
181 $permalink = '<a href="'. $data['guid'] .'" title="'. t('Permalink') .'">'. t('Permalink') .'</a>';
182 }
183 $data['description'] .= PHP_EOL . PHP_EOL . '<br>&#8212; ' . $permalink;
184
185 $data['pub_iso_date'] = $this->getIsoDate($feedType, $data['created']);
186
187 // atom:entry elements MUST contain exactly one atom:updated element.
188 if (!empty($link->getUpdated())) {
189 $data['up_iso_date'] = $this->getIsoDate($feedType, $data['updated'], DateTime::ATOM);
190 } else {
191 $data['up_iso_date'] = $this->getIsoDate($feedType, $data['created'], DateTime::ATOM);
192 }
193
194 // Save the more recent item.
195 if (empty($this->latestDate) || $this->latestDate < $data['created']) {
196 $this->latestDate = $data['created'];
197 }
198 if (!empty($data['updated']) && $this->latestDate < $data['updated']) {
199 $this->latestDate = $data['updated'];
200 }
201
202 return $data;
203 }
204
205 /**
219 * Get the language according to the feed type, based on the locale: 206 * Get the language according to the feed type, based on the locale:
220 * 207 *
221 * - RSS format: en-us (default: 'en-en'). 208 * - RSS format: en-us (default: 'en-en').
222 * - ATOM format: fr (default: 'en'). 209 * - ATOM format: fr (default: 'en').
223 * 210 *
211 * @param string $feedType Type of feed (RSS/ATOM).
212 *
224 * @return string The language. 213 * @return string The language.
225 */ 214 */
226 public function getTypeLanguage() 215 protected function getTypeLanguage(string $feedType)
227 { 216 {
228 // Use the locale do define the language, if available. 217 // Use the locale do define the language, if available.
229 if (!empty($this->locale) && preg_match('/^\w{2}[_\-]\w{2}/', $this->locale)) { 218 if (!empty($this->locale) && preg_match('/^\w{2}[_\-]\w{2}/', $this->locale)) {
230 $length = ($this->feedType === self::$FEED_RSS) ? 5 : 2; 219 $length = ($feedType === self::$FEED_RSS) ? 5 : 2;
231 return str_replace('_', '-', substr($this->locale, 0, $length)); 220 return str_replace('_', '-', substr($this->locale, 0, $length));
232 } 221 }
233 return ($this->feedType === self::$FEED_RSS) ? 'en-en' : 'en'; 222 return ($feedType === self::$FEED_RSS) ? 'en-en' : 'en';
234 } 223 }
235 224
236 /** 225 /**
@@ -238,32 +227,35 @@ class FeedBuilder
238 * 227 *
239 * Return an empty string if invalid DateTime is passed. 228 * Return an empty string if invalid DateTime is passed.
240 * 229 *
230 * @param string $feedType Type of feed (RSS/ATOM).
231 *
241 * @return string Formatted date. 232 * @return string Formatted date.
242 */ 233 */
243 protected function getLatestDateFormatted() 234 protected function getLatestDateFormatted(string $feedType)
244 { 235 {
245 if (empty($this->latestDate) || !$this->latestDate instanceof DateTime) { 236 if (empty($this->latestDate) || !$this->latestDate instanceof DateTime) {
246 return ''; 237 return '';
247 } 238 }
248 239
249 $type = ($this->feedType == self::$FEED_RSS) ? DateTime::RSS : DateTime::ATOM; 240 $type = ($feedType == self::$FEED_RSS) ? DateTime::RSS : DateTime::ATOM;
250 return $this->latestDate->format($type); 241 return $this->latestDate->format($type);
251 } 242 }
252 243
253 /** 244 /**
254 * Get ISO date from DateTime according to feed type. 245 * Get ISO date from DateTime according to feed type.
255 * 246 *
247 * @param string $feedType Type of feed (RSS/ATOM).
256 * @param DateTime $date Date to format. 248 * @param DateTime $date Date to format.
257 * @param string|bool $format Force format. 249 * @param string|bool $format Force format.
258 * 250 *
259 * @return string Formatted date. 251 * @return string Formatted date.
260 */ 252 */
261 protected function getIsoDate(DateTime $date, $format = false) 253 protected function getIsoDate(string $feedType, DateTime $date, $format = false)
262 { 254 {
263 if ($format !== false) { 255 if ($format !== false) {
264 return $date->format($format); 256 return $date->format($format);
265 } 257 }
266 if ($this->feedType == self::$FEED_RSS) { 258 if ($feedType == self::$FEED_RSS) {
267 return $date->format(DateTime::RSS); 259 return $date->format(DateTime::RSS);
268 } 260 }
269 return $date->format(DateTime::ATOM); 261 return $date->format(DateTime::ATOM);
@@ -273,23 +265,24 @@ class FeedBuilder
273 * Returns the number of link to display according to 'nb' user input parameter. 265 * Returns the number of link to display according to 'nb' user input parameter.
274 * 266 *
275 * If 'nb' not set or invalid, default value: $DEFAULT_NB_LINKS. 267 * If 'nb' not set or invalid, default value: $DEFAULT_NB_LINKS.
276 * If 'nb' is set to 'all', display all filtered links (max parameter). 268 * If 'nb' is set to 'all', display all filtered bookmarks (max parameter).
277 * 269 *
278 * @param int $max maximum number of links to display. 270 * @param int $max maximum number of bookmarks to display.
271 * @param array $userInput $_GET.
279 * 272 *
280 * @return int number of links to display. 273 * @return int number of bookmarks to display.
281 */ 274 */
282 public function getNbLinks($max) 275 protected function getNbLinks($max, ?array $userInput)
283 { 276 {
284 if (empty($this->userInput['nb'])) { 277 if (empty($userInput['nb'])) {
285 return self::$DEFAULT_NB_LINKS; 278 return self::$DEFAULT_NB_LINKS;
286 } 279 }
287 280
288 if ($this->userInput['nb'] == 'all') { 281 if ($userInput['nb'] == 'all') {
289 return $max; 282 return $max;
290 } 283 }
291 284
292 $intNb = intval($this->userInput['nb']); 285 $intNb = intval($userInput['nb']);
293 if (!is_int($intNb) || $intNb == 0) { 286 if (!is_int($intNb) || $intNb == 0) {
294 return self::$DEFAULT_NB_LINKS; 287 return self::$DEFAULT_NB_LINKS;
295 } 288 }