]> git.immae.eu Git - github/wallabag/wallabag.git/blob - inc/3rdparty/libraries/feedwriter/FeedWriter.php
update to 3.2 version of full-text-rss, issue #694
[github/wallabag/wallabag.git] / inc / 3rdparty / libraries / feedwriter / FeedWriter.php
1 <?php
2 define('RSS2', 1, true);
3 define('JSON', 2, true);
4 define('JSONP', 3, true);
5
6 /**
7 * Univarsel Feed Writer class
8 *
9 * Genarate RSS2 or JSON (original: RSS 1.0, RSS2.0 and ATOM Feed)
10 *
11 * Modified for FiveFilters.org's Full-Text RSS project
12 * to allow for inclusion of hubs, JSON output.
13 * Stripped RSS1 and ATOM support.
14 *
15 * @package UnivarselFeedWriter
16 * @author Anis uddin Ahmad <anisniit@gmail.com>
17 * @link http://www.ajaxray.com/projects/rss
18 */
19 class FeedWriter
20 {
21 private $self = null; // self URL - http://feed2.w3.org/docs/warning/MissingAtomSelfLink.html
22 private $hubs = array(); // PubSubHubbub hubs
23 private $channels = array(); // Collection of channel elements
24 private $items = array(); // Collection of items as object of FeedItem class.
25 private $data = array(); // Store some other version wise data
26 private $CDATAEncoding = array(); // The tag names which have to encoded as CDATA
27 private $xsl = null; // stylesheet to render RSS (used by Chrome)
28 private $json = null; // JSON object
29
30 private $version = null;
31
32 /**
33 * Constructor
34 *
35 * @param constant the version constant (RSS2 or JSON).
36 */
37 function __construct($version = RSS2)
38 {
39 $this->version = $version;
40
41 // Setting default value for assential channel elements
42 $this->channels['title'] = $version . ' Feed';
43 $this->channels['link'] = 'http://www.ajaxray.com/blog';
44
45 //Tag names to encode in CDATA
46 $this->CDATAEncoding = array('description', 'content:encoded', 'content', 'subtitle', 'summary');
47 }
48
49 public function setFormat($format) {
50 $this->version = $format;
51 }
52
53 // Start # public functions ---------------------------------------------
54
55 /**
56 * Set a channel element
57 * @access public
58 * @param srting name of the channel tag
59 * @param string content of the channel tag
60 * @return void
61 */
62 public function setChannelElement($elementName, $content)
63 {
64 $this->channels[$elementName] = $content ;
65 }
66
67 /**
68 * Set multiple channel elements from an array. Array elements
69 * should be 'channelName' => 'channelContent' format.
70 *
71 * @access public
72 * @param array array of channels
73 * @return void
74 */
75 public function setChannelElementsFromArray($elementArray)
76 {
77 if(! is_array($elementArray)) return;
78 foreach ($elementArray as $elementName => $content)
79 {
80 $this->setChannelElement($elementName, $content);
81 }
82 }
83
84 /**
85 * Genarate the actual RSS/JSON file
86 *
87 * @access public
88 * @return void
89 */
90 public function genarateFeed($withHeaders = true)
91 {
92 if ($withHeaders) {
93 if ($this->version == RSS2) {
94 header('Content-type: text/xml; charset=UTF-8');
95 // this line prevents Chrome 20 from prompting download
96 // used by Google: https://news.google.com/news/feeds?ned=us&topic=b&output=rss
97 header('X-content-type-options: nosniff');
98 } elseif ($this->version == JSON) {
99 header('Content-type: application/json; charset=UTF-8');
100 $this->json = new stdClass();
101 } elseif ($this->version == JSONP) {
102 header('Content-type: application/javascript; charset=UTF-8');
103 $this->json = new stdClass();
104 }
105 }
106
107 $this->printHead();
108 $this->printChannels();
109 $this->printItems();
110 $this->printTale();
111 if ($this->version == JSON || $this->version == JSONP) {
112 echo json_encode($this->json);
113 }
114 }
115
116 public function &getItems()
117 {
118 return $this->items;
119 }
120
121 /**
122 * Create a new FeedItem.
123 *
124 * @access public
125 * @return object instance of FeedItem class
126 */
127 public function createNewItem()
128 {
129 $Item = new FeedItem($this->version);
130 return $Item;
131 }
132
133 /**
134 * Add a FeedItem to the main class
135 *
136 * @access public
137 * @param object instance of FeedItem class
138 * @return void
139 */
140 public function addItem($feedItem)
141 {
142 $this->items[] = $feedItem;
143 }
144
145 // Wrapper functions -------------------------------------------------------------------
146
147 /**
148 * Set the 'title' channel element
149 *
150 * @access public
151 * @param srting value of 'title' channel tag
152 * @return void
153 */
154 public function setTitle($title)
155 {
156 $this->setChannelElement('title', $title);
157 }
158
159 /**
160 * Add a hub to the channel element
161 *
162 * @access public
163 * @param string URL
164 * @return void
165 */
166 public function addHub($hub)
167 {
168 $this->hubs[] = $hub;
169 }
170
171 /**
172 * Set XSL URL
173 *
174 * @access public
175 * @param string URL
176 * @return void
177 */
178 public function setXsl($xsl)
179 {
180 $this->xsl = $xsl;
181 }
182
183 /**
184 * Set self URL
185 *
186 * @access public
187 * @param string URL
188 * @return void
189 */
190 public function setSelf($self)
191 {
192 $this->self = $self;
193 }
194
195 /**
196 * Set the 'description' channel element
197 *
198 * @access public
199 * @param srting value of 'description' channel tag
200 * @return void
201 */
202 public function setDescription($description)
203 {
204 $tag = ($this->version == ATOM)? 'subtitle' : 'description';
205 $this->setChannelElement($tag, $desciption);
206 }
207
208 /**
209 * Set the 'link' channel element
210 *
211 * @access public
212 * @param srting value of 'link' channel tag
213 * @return void
214 */
215 public function setLink($link)
216 {
217 $this->setChannelElement('link', $link);
218 }
219
220 /**
221 * Set the 'image' channel element
222 *
223 * @access public
224 * @param srting title of image
225 * @param srting link url of the imahe
226 * @param srting path url of the image
227 * @return void
228 */
229 public function setImage($title, $link, $url)
230 {
231 $this->setChannelElement('image', array('title'=>$title, 'link'=>$link, 'url'=>$url));
232 }
233
234 // End # public functions ----------------------------------------------
235
236 // Start # private functions ----------------------------------------------
237
238 /**
239 * Prints the xml and rss namespace
240 *
241 * @access private
242 * @return void
243 */
244 private function printHead()
245 {
246 if ($this->version == RSS2)
247 {
248 $out = '<?xml version="1.0" encoding="utf-8"?>'."\n";
249 if ($this->xsl) $out .= '<?xml-stylesheet type="text/xsl" href="'.htmlspecialchars($this->xsl).'"?>' . PHP_EOL;
250 $out .= '<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">' . PHP_EOL;
251 echo $out;
252 }
253 elseif ($this->version == JSON || $this->version == JSONP)
254 {
255 $this->json->rss = array('@attributes' => array('version' => '2.0'));
256 }
257 }
258
259 /**
260 * Closes the open tags at the end of file
261 *
262 * @access private
263 * @return void
264 */
265 private function printTale()
266 {
267 if ($this->version == RSS2)
268 {
269 echo '</channel>',PHP_EOL,'</rss>';
270 }
271 // do nothing for JSON
272 }
273
274 /**
275 * Creates a single node as xml format
276 *
277 * @access private
278 * @param string name of the tag
279 * @param mixed tag value as string or array of nested tags in 'tagName' => 'tagValue' format
280 * @param array Attributes(if any) in 'attrName' => 'attrValue' format
281 * @return string formatted xml tag
282 */
283 private function makeNode($tagName, $tagContent, $attributes = null)
284 {
285 if ($this->version == RSS2)
286 {
287 $nodeText = '';
288 $attrText = '';
289 if (is_array($attributes))
290 {
291 foreach ($attributes as $key => $value)
292 {
293 $attrText .= " $key=\"$value\" ";
294 }
295 }
296 $nodeText .= "<{$tagName}{$attrText}>";
297 if (is_array($tagContent))
298 {
299 foreach ($tagContent as $key => $value)
300 {
301 $nodeText .= $this->makeNode($key, $value);
302 }
303 }
304 else
305 {
306 //$nodeText .= (in_array($tagName, $this->CDATAEncoding))? $tagContent : htmlentities($tagContent);
307 $nodeText .= htmlspecialchars($tagContent);
308 }
309 //$nodeText .= (in_array($tagName, $this->CDATAEncoding))? "]]></$tagName>" : "</$tagName>";
310 $nodeText .= "</$tagName>";
311 return $nodeText . PHP_EOL;
312 }
313 elseif ($this->version == JSON || $this->version == JSONP)
314 {
315 $tagName = (string)$tagName;
316 $tagName = strtr($tagName, ':', '_');
317 $node = null;
318 if (!$tagContent && is_array($attributes) && count($attributes))
319 {
320 $node = array('@attributes' => $this->json_keys($attributes));
321 } else {
322 if (is_array($tagContent)) {
323 $node = $this->json_keys($tagContent);
324 } else {
325 $node = $tagContent;
326 }
327 }
328 return $node;
329 }
330 return ''; // should not get here
331 }
332
333 private function json_keys(array $array) {
334 $new = array();
335 foreach ($array as $key => $val) {
336 if (is_string($key)) $key = strtr($key, ':', '_');
337 if (is_array($val)) {
338 $new[$key] = $this->json_keys($val);
339 } else {
340 $new[$key] = $val;
341 }
342 }
343 return $new;
344 }
345
346 /**
347 * @desc Print channels
348 * @access private
349 * @return void
350 */
351 private function printChannels()
352 {
353 //Start channel tag
354 if ($this->version == RSS2) {
355 echo '<channel>' . PHP_EOL;
356 // add hubs
357 foreach ($this->hubs as $hub) {
358 //echo $this->makeNode('link', '', array('rel'=>'hub', 'href'=>$hub, 'xmlns'=>'http://www.w3.org/2005/Atom'));
359 echo '<link rel="hub" href="'.htmlspecialchars($hub).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
360 }
361 // add self
362 if (isset($this->self)) {
363 //echo $this->makeNode('link', '', array('rel'=>'self', 'href'=>$this->self, 'xmlns'=>'http://www.w3.org/2005/Atom'));
364 echo '<link rel="self" href="'.htmlspecialchars($this->self).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
365 }
366 //Print Items of channel
367 foreach ($this->channels as $key => $value)
368 {
369 echo $this->makeNode($key, $value);
370 }
371 } elseif ($this->version == JSON || $this->version == JSONP) {
372 $this->json->rss['channel'] = (object)$this->json_keys($this->channels);
373 }
374 }
375
376 /**
377 * Prints formatted feed items
378 *
379 * @access private
380 * @return void
381 */
382 private function printItems()
383 {
384 foreach ($this->items as $item) {
385 $itemElements = $item->getElements();
386
387 echo $this->startItem();
388
389 if ($this->version == JSON || $this->version == JSONP) {
390 $json_item = array();
391 }
392
393 foreach ($itemElements as $thisElement) {
394 foreach ($thisElement as $instance) {
395 if ($this->version == RSS2) {
396 echo $this->makeNode($instance['name'], $instance['content'], $instance['attributes']);
397 } elseif ($this->version == JSON || $this->version == JSONP) {
398 $_json_node = $this->makeNode($instance['name'], $instance['content'], $instance['attributes']);
399 if (count($thisElement) > 1) {
400 $json_item[strtr($instance['name'], ':', '_')][] = $_json_node;
401 } else {
402 $json_item[strtr($instance['name'], ':', '_')] = $_json_node;
403 }
404 }
405 }
406 }
407 echo $this->endItem();
408 if ($this->version == JSON || $this->version == JSONP) {
409 if (count($this->items) > 1) {
410 $this->json->rss['channel']->item[] = $json_item;
411 } else {
412 $this->json->rss['channel']->item = $json_item;
413 }
414 }
415 }
416 }
417
418 /**
419 * Make the starting tag of channels
420 *
421 * @access private
422 * @return void
423 */
424 private function startItem()
425 {
426 if ($this->version == RSS2)
427 {
428 echo '<item>' . PHP_EOL;
429 }
430 // nothing for JSON
431 }
432
433 /**
434 * Closes feed item tag
435 *
436 * @access private
437 * @return void
438 */
439 private function endItem()
440 {
441 if ($this->version == RSS2)
442 {
443 echo '</item>' . PHP_EOL;
444 }
445 // nothing for JSON
446 }
447
448 // End # private functions ----------------------------------------------
449 }