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