]> git.immae.eu Git - github/wallabag/wallabag.git/blob - inc/3rdparty/libraries/feedwriter/FeedWriter.php
[fix] rss feed content type set to text/xml #636
[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()
91 {
92 header('Content-type: text/xml; charset=UTF-8');
93 // this line prevents Chrome 20 from prompting download
94 // used by Google: https://news.google.com/news/feeds?ned=us&topic=b&output=rss
95 header('X-content-type-options: nosniff');
96
97 $this->printHead();
98 $this->printChannels();
99 $this->printItems();
100 $this->printTale();
101 if ($this->version == JSON || $this->version == JSONP) {
102 echo json_encode($this->json);
103 }
104 }
105
106 /**
107 * Create a new FeedItem.
108 *
109 * @access public
110 * @return object instance of FeedItem class
111 */
112 public function createNewItem()
113 {
114 $Item = new FeedItem($this->version);
115 return $Item;
116 }
117
118 /**
119 * Add a FeedItem to the main class
120 *
121 * @access public
122 * @param object instance of FeedItem class
123 * @return void
124 */
125 public function addItem($feedItem)
126 {
127 $this->items[] = $feedItem;
128 }
129
130 // Wrapper functions -------------------------------------------------------------------
131
132 /**
133 * Set the 'title' channel element
134 *
135 * @access public
136 * @param srting value of 'title' channel tag
137 * @return void
138 */
139 public function setTitle($title)
140 {
141 $this->setChannelElement('title', $title);
142 }
143
144 /**
145 * Add a hub to the channel element
146 *
147 * @access public
148 * @param string URL
149 * @return void
150 */
151 public function addHub($hub)
152 {
153 $this->hubs[] = $hub;
154 }
155
156 /**
157 * Set XSL URL
158 *
159 * @access public
160 * @param string URL
161 * @return void
162 */
163 public function setXsl($xsl)
164 {
165 $this->xsl = $xsl;
166 }
167
168 /**
169 * Set self URL
170 *
171 * @access public
172 * @param string URL
173 * @return void
174 */
175 public function setSelf($self)
176 {
177 $this->self = $self;
178 }
179
180 /**
181 * Set the 'description' channel element
182 *
183 * @access public
184 * @param srting value of 'description' channel tag
185 * @return void
186 */
187 public function setDescription($description)
188 {
189 $this->setChannelElement('description', $description);
190 }
191
192 /**
193 * Set the 'link' channel element
194 *
195 * @access public
196 * @param srting value of 'link' channel tag
197 * @return void
198 */
199 public function setLink($link)
200 {
201 $this->setChannelElement('link', $link);
202 }
203
204 /**
205 * Set the 'image' channel element
206 *
207 * @access public
208 * @param srting title of image
209 * @param srting link url of the imahe
210 * @param srting path url of the image
211 * @return void
212 */
213 public function setImage($title, $link, $url)
214 {
215 $this->setChannelElement('image', array('title'=>$title, 'link'=>$link, 'url'=>$url));
216 }
217
218 // End # public functions ----------------------------------------------
219
220 // Start # private functions ----------------------------------------------
221
222 /**
223 * Prints the xml and rss namespace
224 *
225 * @access private
226 * @return void
227 */
228 private function printHead()
229 {
230 if ($this->version == RSS2)
231 {
232 $out = '<?xml version="1.0" encoding="utf-8"?>'."\n";
233 if ($this->xsl) $out .= '<?xml-stylesheet type="text/xsl" href="'.htmlspecialchars($this->xsl).'"?>' . PHP_EOL;
234 $out .= '<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">' . PHP_EOL;
235 echo $out;
236 }
237 elseif ($this->version == JSON || $this->version == JSONP)
238 {
239 $this->json->rss = array('@attributes' => array('version' => '2.0'));
240 }
241 }
242
243 /**
244 * Closes the open tags at the end of file
245 *
246 * @access private
247 * @return void
248 */
249 private function printTale()
250 {
251 if ($this->version == RSS2)
252 {
253 echo '</channel>',PHP_EOL,'</rss>';
254 }
255 // do nothing for JSON
256 }
257
258 /**
259 * Creates a single node as xml format
260 *
261 * @access private
262 * @param string name of the tag
263 * @param mixed tag value as string or array of nested tags in 'tagName' => 'tagValue' format
264 * @param array Attributes(if any) in 'attrName' => 'attrValue' format
265 * @return string formatted xml tag
266 */
267 private function makeNode($tagName, $tagContent, $attributes = null)
268 {
269 if ($this->version == RSS2)
270 {
271 $nodeText = '';
272 $attrText = '';
273 if (is_array($attributes))
274 {
275 foreach ($attributes as $key => $value)
276 {
277 $attrText .= " $key=\"$value\" ";
278 }
279 }
280 $nodeText .= "<{$tagName}{$attrText}>";
281 if (is_array($tagContent))
282 {
283 foreach ($tagContent as $key => $value)
284 {
285 $nodeText .= $this->makeNode($key, $value);
286 }
287 }
288 else
289 {
290 //$nodeText .= (in_array($tagName, $this->CDATAEncoding))? $tagContent : htmlentities($tagContent);
291 $nodeText .= htmlspecialchars($tagContent);
292 }
293 //$nodeText .= (in_array($tagName, $this->CDATAEncoding))? "]]></$tagName>" : "</$tagName>";
294 $nodeText .= "</$tagName>";
295 return $nodeText . PHP_EOL;
296 }
297 elseif ($this->version == JSON || $this->version == JSONP)
298 {
299 $tagName = (string)$tagName;
300 $tagName = strtr($tagName, ':', '_');
301 $node = null;
302 if (!$tagContent && is_array($attributes) && count($attributes))
303 {
304 $node = array('@attributes' => $this->json_keys($attributes));
305 } else {
306 if (is_array($tagContent)) {
307 $node = $this->json_keys($tagContent);
308 } else {
309 $node = $tagContent;
310 }
311 }
312 return $node;
313 }
314 return ''; // should not get here
315 }
316
317 private function json_keys(array $array) {
318 $new = array();
319 foreach ($array as $key => $val) {
320 if (is_string($key)) $key = strtr($key, ':', '_');
321 if (is_array($val)) {
322 $new[$key] = $this->json_keys($val);
323 } else {
324 $new[$key] = $val;
325 }
326 }
327 return $new;
328 }
329
330 /**
331 * @desc Print channels
332 * @access private
333 * @return void
334 */
335 private function printChannels()
336 {
337 //Start channel tag
338 if ($this->version == RSS2) {
339 echo '<channel>' . PHP_EOL;
340 // add hubs
341 foreach ($this->hubs as $hub) {
342 //echo $this->makeNode('link', '', array('rel'=>'hub', 'href'=>$hub, 'xmlns'=>'http://www.w3.org/2005/Atom'));
343 echo '<link rel="hub" href="'.htmlspecialchars($hub).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
344 }
345 // add self
346 if (isset($this->self)) {
347 //echo $this->makeNode('link', '', array('rel'=>'self', 'href'=>$this->self, 'xmlns'=>'http://www.w3.org/2005/Atom'));
348 echo '<link rel="self" href="'.htmlspecialchars($this->self).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
349 }
350 //Print Items of channel
351 foreach ($this->channels as $key => $value)
352 {
353 echo $this->makeNode($key, $value);
354 }
355 } elseif ($this->version == JSON || $this->version == JSONP) {
356 $this->json->rss['channel'] = (object)$this->json_keys($this->channels);
357 }
358 }
359
360 /**
361 * Prints formatted feed items
362 *
363 * @access private
364 * @return void
365 */
366 private function printItems()
367 {
368 foreach ($this->items as $item) {
369 $itemElements = $item->getElements();
370
371 echo $this->startItem();
372
373 if ($this->version == JSON || $this->version == JSONP) {
374 $json_item = array();
375 }
376
377 foreach ($itemElements as $thisElement) {
378 foreach ($thisElement as $instance) {
379 if ($this->version == RSS2) {
380 echo $this->makeNode($instance['name'], $instance['content'], $instance['attributes']);
381 } elseif ($this->version == JSON || $this->version == JSONP) {
382 $_json_node = $this->makeNode($instance['name'], $instance['content'], $instance['attributes']);
383 if (count($thisElement) > 1) {
384 $json_item[strtr($instance['name'], ':', '_')][] = $_json_node;
385 } else {
386 $json_item[strtr($instance['name'], ':', '_')] = $_json_node;
387 }
388 }
389 }
390 }
391 echo $this->endItem();
392 if ($this->version == JSON || $this->version == JSONP) {
393 if (count($this->items) > 1) {
394 $this->json->rss['channel']->item[] = $json_item;
395 } else {
396 $this->json->rss['channel']->item = $json_item;
397 }
398 }
399 }
400 }
401
402 /**
403 * Make the starting tag of channels
404 *
405 * @access private
406 * @return void
407 */
408 private function startItem()
409 {
410 if ($this->version == RSS2)
411 {
412 echo '<item>' . PHP_EOL;
413 }
414 // nothing for JSON
415 }
416
417 /**
418 * Closes feed item tag
419 *
420 * @access private
421 * @return void
422 */
423 private function endItem()
424 {
425 if ($this->version == RSS2)
426 {
427 echo '</item>' . PHP_EOL;
428 }
429 // nothing for JSON
430 }
431
432 // End # private functions ----------------------------------------------
433 }