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