Mereged updates from DokuWiki 38
[sudaraka-org:dokuwiki-mods.git] / inc / feedcreator.class.php
1 <?php
2 /***************************************************************************
3  * FeedCreator class v1.7.2-ppt
4  * originally (c) Kai Blankenhorn
5  * www.bitfolge.de
6  * kaib@bitfolge.de
7  * v1.3 work by Scott Reynen (scott@randomchaos.com) and Kai Blankenhorn
8  * v1.5 OPML support by Dirk Clemens
9  * v1.7.2-mod on-the-fly feed generation by Fabian Wolf (info@f2w.de)
10  * v1.7.2-ppt ATOM 1.0 support by Mohammad Hafiz bin Ismail (mypapit@gmail.com)
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25  *
26  * @author www.bitfolge.de
27  *
28  * Changelog:
29  *
30  * this version contains some smaller modifications for DokuWiki as well
31  *
32  * v1.7.2-ppt   11-21-05
33  *  added Atom 1.0 support
34  *  added enclosure support for RSS 2.0/ATOM 1.0
35  *  added docs for v1.7.2-ppt only!
36  *
37  * v1.7.2-mod   03-12-05
38  *  added output function outputFeed for on-the-fly feed generation
39  *
40  * v1.7.2   10-11-04
41  *  license changed to LGPL
42  *
43  * v1.7.1
44  *  fixed a syntax bug
45  *  fixed left over debug code
46  *
47  * v1.7 07-18-04
48  *  added HTML and JavaScript feeds (configurable via CSS) (thanks to Pascal Van Hecke)
49  *  added HTML descriptions for all feed formats (thanks to Pascal Van Hecke)
50  *  added a switch to select an external stylesheet (thanks to Pascal Van Hecke)
51  *  changed default content-type to application/xml
52  *  added character encoding setting
53  *  fixed numerous smaller bugs (thanks to Sören Fuhrmann of golem.de)
54  *  improved changing ATOM versions handling (thanks to August Trometer)
55  *  improved the UniversalFeedCreator's useCached method (thanks to Sören Fuhrmann of golem.de)
56  *  added charset output in HTTP headers (thanks to Sören Fuhrmann of golem.de)
57  *  added Slashdot namespace to RSS 1.0 (thanks to Sören Fuhrmann of golem.de)
58  *
59  * See www.bitfolge.de for additional changelog info
60  */
61 // your local timezone, set to "" to disable or for GMT
62 define("TIME_ZONE",date("O", time()));
63
64
65
66
67 /**
68  * Version string.
69  **/
70
71 define("FEEDCREATOR_VERSION", "FeedCreator 1.7.2-ppt DokuWiki");
72
73
74
75 /**
76  * A FeedItem is a part of a FeedCreator feed.
77  *
78  * @author Kai Blankenhorn <kaib@bitfolge.de>
79  * @since 1.3
80  */
81 class FeedItem extends HtmlDescribable {
82     /**
83      * Mandatory attributes of an item.
84      */
85     var $title, $description, $link;
86
87     /**
88      * Optional attributes of an item.
89      */
90     var $author, $authorEmail, $image, $category, $comments, $guid, $source, $creator;
91
92     /**
93      * Publishing date of an item. May be in one of the following formats:
94      *
95      *  RFC 822:
96      *  "Mon, 20 Jan 03 18:05:41 +0400"
97      *  "20 Jan 03 18:05:41 +0000"
98      *
99      *  ISO 8601:
100      *  "2003-01-20T18:05:41+04:00"
101      *
102      *  Unix:
103      *  1043082341
104      */
105     var $date;
106
107     /**
108      * Add <enclosure> element tag RSS 2.0
109      * modified by : Mohammad Hafiz bin Ismail (mypapit@gmail.com)
110      *
111      *
112      * display :
113      * <enclosure length="17691" url="http://something.com/picture.jpg" type="image/jpeg" />
114      *
115      */
116     var $enclosure;
117
118     /**
119      * Any additional elements to include as an assiciated array. All $key => $value pairs
120      * will be included unencoded in the feed item in the form
121      *     <$key>$value</$key>
122      * Again: No encoding will be used! This means you can invalidate or enhance the feed
123      * if $value contains markup. This may be abused to embed tags not implemented by
124      * the FeedCreator class used.
125      */
126     var $additionalElements = Array();
127
128     // on hold
129     // var $source;
130 }
131
132 class EnclosureItem extends HtmlDescribable {
133     /*
134     *
135     * core variables
136     *
137     **/
138     var $url,$length,$type;
139
140     /*
141     * For use with another extension like Yahoo mRSS
142     * Warning :
143     * These variables might not show up in
144     * later release / not finalize yet!
145     *
146     */
147     var $width, $height, $title, $description, $keywords, $thumburl;
148
149     var $additionalElements = Array();
150
151 }
152
153
154 /**
155  * An FeedImage may be added to a FeedCreator feed.
156  * @author Kai Blankenhorn <kaib@bitfolge.de>
157  * @since 1.3
158  */
159 class FeedImage extends HtmlDescribable {
160     /**
161      * Mandatory attributes of an image.
162      */
163     var $title, $url, $link;
164
165     /**
166      * Optional attributes of an image.
167      */
168     var $width, $height, $description;
169 }
170
171
172
173 /**
174  * An HtmlDescribable is an item within a feed that can have a description that may
175  * include HTML markup.
176  */
177 class HtmlDescribable {
178     /**
179      * Indicates whether the description field should be rendered in HTML.
180      */
181     var $descriptionHtmlSyndicated;
182
183     /**
184      * Indicates whether and to how many characters a description should be truncated.
185      */
186     var $descriptionTruncSize;
187
188     /**
189      * Returns a formatted description field, depending on descriptionHtmlSyndicated and
190      * $descriptionTruncSize properties
191      * @return    string    the formatted description
192      */
193     function getDescription() {
194         $descriptionField = new FeedHtmlField($this->description);
195         $descriptionField->syndicateHtml = $this->descriptionHtmlSyndicated;
196         $descriptionField->truncSize = $this->descriptionTruncSize;
197         return $descriptionField->output();
198     }
199
200 }
201
202
203
204 /**
205  * An FeedHtmlField describes and generates
206  * a feed, item or image html field (probably a description). Output is
207  * generated based on $truncSize, $syndicateHtml properties.
208  * @author Pascal Van Hecke <feedcreator.class.php@vanhecke.info>
209  * @version 1.6
210  */
211 class FeedHtmlField {
212     /**
213      * Mandatory attributes of a FeedHtmlField.
214      */
215     var $rawFieldContent;
216
217     /**
218      * Optional attributes of a FeedHtmlField.
219      *
220      */
221     var $truncSize, $syndicateHtml;
222
223     /**
224      * Creates a new instance of FeedHtmlField.
225      * @param  $string: if given, sets the rawFieldContent property
226      */
227     function FeedHtmlField($parFieldContent) {
228         if ($parFieldContent) {
229             $this->rawFieldContent = $parFieldContent;
230         }
231     }
232
233
234     /**
235      * Creates the right output, depending on $truncSize, $syndicateHtml properties.
236      * @return string    the formatted field
237      */
238     function output() {
239         // when field available and syndicated in html we assume
240         // - valid html in $rawFieldContent and we enclose in CDATA tags
241         // - no truncation (truncating risks producing invalid html)
242         if (!$this->rawFieldContent) {
243             $result = "";
244         }   elseif ($this->syndicateHtml) {
245             $result = "<![CDATA[".$this->rawFieldContent."]]>";
246         } else {
247             if ($this->truncSize and is_int($this->truncSize)) {
248                 $result = FeedCreator::iTrunc(htmlspecialchars($this->rawFieldContent),$this->truncSize);
249             } else {
250                 $result = htmlspecialchars($this->rawFieldContent);
251             }
252         }
253         return $result;
254     }
255
256 }
257
258
259
260 /**
261  * UniversalFeedCreator lets you choose during runtime which
262  * format to build.
263  * For general usage of a feed class, see the FeedCreator class
264  * below or the example above.
265  *
266  * @since 1.3
267  * @author Kai Blankenhorn <kaib@bitfolge.de>
268  */
269 class UniversalFeedCreator extends FeedCreator {
270     var $_feed;
271
272     function _setFormat($format) {
273         switch (strtoupper($format)) {
274
275             case "2.0":
276                 // fall through
277             case "RSS2.0":
278                 $this->_feed = new RSSCreator20();
279                 break;
280
281             case "1.0":
282                 // fall through
283             case "RSS1.0":
284                 $this->_feed = new RSSCreator10();
285                 break;
286
287             case "0.91":
288                 // fall through
289             case "RSS0.91":
290                 $this->_feed = new RSSCreator091();
291                 break;
292
293             case "PIE0.1":
294                 $this->_feed = new PIECreator01();
295                 break;
296
297             case "MBOX":
298                 $this->_feed = new MBOXCreator();
299                 break;
300
301             case "OPML":
302                 $this->_feed = new OPMLCreator();
303                 break;
304
305             case "ATOM":
306                 // fall through: always the latest ATOM version
307             case "ATOM1.0":
308                 $this->_feed = new AtomCreator10();
309                 break;
310
311             case "ATOM0.3":
312                 $this->_feed = new AtomCreator03();
313                 break;
314
315             case "HTML":
316                 $this->_feed = new HTMLCreator();
317                 break;
318
319             case "JS":
320                 // fall through
321             case "JAVASCRIPT":
322                 $this->_feed = new JSCreator();
323                 break;
324
325             default:
326                 $this->_feed = new RSSCreator091();
327                 break;
328         }
329
330         $vars = get_object_vars($this);
331         foreach ($vars as $key => $value) {
332             // prevent overwriting of properties "contentType", "encoding"; do not copy "_feed" itself
333             if (!in_array($key, array("_feed", "contentType", "encoding"))) {
334                 $this->_feed->{$key} = $this->{$key};
335             }
336         }
337     }
338
339     function _sendMIME() {
340         header('Content-Type: '.$this->contentType.'; charset='.$this->encoding, true);
341     }
342
343     /**
344      * Creates a syndication feed based on the items previously added.
345      *
346      * @see        FeedCreator::addItem()
347      * @param    string    format    format the feed should comply to. Valid values are:
348      *          "PIE0.1", "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3", "HTML", "JS"
349      * @return    string    the contents of the feed.
350      */
351     function createFeed($format = "RSS0.91") {
352         $this->_setFormat($format);
353         return $this->_feed->createFeed();
354     }
355
356     /**
357      * Saves this feed as a file on the local disk. After the file is saved, an HTTP redirect
358      * header may be sent to redirect the use to the newly created file.
359      * @since 1.4
360      *
361      * @param   string  format  format the feed should comply to. Valid values are:
362      *          "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM", "ATOM0.3", "HTML", "JS"
363      * @param   string  filename    optional    the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
364      * @param   boolean displayContents optional    send the content of the file or not. If true, the file will be sent in the body of the response.
365      */
366     function saveFeed($format="RSS0.91", $filename="", $displayContents=true) {
367         $this->_setFormat($format);
368         $this->_feed->saveFeed($filename, $displayContents);
369     }
370
371
372     /**
373      * Turns on caching and checks if there is a recent version of this feed in the cache.
374      * If there is, an HTTP redirect header is sent.
375      * To effectively use caching, you should create the FeedCreator object and call this method
376      * before anything else, especially before you do the time consuming task to build the feed
377      * (web fetching, for example).
378      *
379      * @param   string   format   format the feed should comply to. Valid values are:
380      *       "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3".
381      * @param filename   string   optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
382      * @param timeout int      optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour)
383      */
384     function useCached($format="RSS0.91", $filename="", $timeout=3600) {
385         $this->_setFormat($format);
386         $this->_feed->useCached($filename, $timeout);
387     }
388
389
390     /**
391      * Outputs feed to the browser - needed for on-the-fly feed generation (like it is done in WordPress, etc.)
392      *
393      * @param    format  string  format the feed should comply to. Valid values are:
394      *                           "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3".
395      */
396     function outputFeed($format='RSS0.91') {
397         $this->_setFormat($format);
398         $this->_sendMIME();
399         $this->_feed->outputFeed();
400     }
401
402
403 }
404
405
406 /**
407  * FeedCreator is the abstract base implementation for concrete
408  * implementations that implement a specific format of syndication.
409  *
410  * @abstract
411  * @author Kai Blankenhorn <kaib@bitfolge.de>
412  * @since 1.4
413  */
414 class FeedCreator extends HtmlDescribable {
415
416     /**
417      * Mandatory attributes of a feed.
418      */
419     var $title, $description, $link;
420
421
422     /**
423      * Optional attributes of a feed.
424      */
425     var $syndicationURL, $image, $language, $copyright, $pubDate, $lastBuildDate, $editor, $editorEmail, $webmaster, $category, $docs, $ttl, $rating, $skipHours, $skipDays;
426
427     /**
428     * The url of the external xsl stylesheet used to format the naked rss feed.
429     * Ignored in the output when empty.
430     */
431     var $xslStyleSheet = "";
432
433
434     /**
435      * @access private
436      */
437     var $items = Array();
438
439
440     /**
441      * This feed's MIME content type.
442      * @since 1.4
443      * @access private
444      */
445     var $contentType = "application/xml";
446
447
448     /**
449      * This feed's character encoding.
450      * @since 1.6.1
451      **/
452     var $encoding = "utf-8";
453
454
455     /**
456      * Any additional elements to include as an assiciated array. All $key => $value pairs
457      * will be included unencoded in the feed in the form
458      *     <$key>$value</$key>
459      * Again: No encoding will be used! This means you can invalidate or enhance the feed
460      * if $value contains markup. This may be abused to embed tags not implemented by
461      * the FeedCreator class used.
462      */
463     var $additionalElements = Array();
464
465
466     /**
467      * Adds an FeedItem to the feed.
468      *
469      * @param object FeedItem $item The FeedItem to add to the feed.
470      * @access public
471      */
472     function addItem($item) {
473         $this->items[] = $item;
474     }
475
476
477     /**
478      * Truncates a string to a certain length at the most sensible point.
479      * First, if there's a '.' character near the end of the string, the string is truncated after this character.
480      * If there is no '.', the string is truncated after the last ' ' character.
481      * If the string is truncated, " ..." is appended.
482      * If the string is already shorter than $length, it is returned unchanged.
483      *
484      * @static
485      * @param string    string A string to be truncated.
486      * @param int        length the maximum length the string should be truncated to
487      * @return string    the truncated string
488      */
489     function iTrunc($string, $length) {
490         if (strlen($string)<=$length) {
491             return $string;
492         }
493
494         $pos = strrpos($string,".");
495         if ($pos>=$length-4) {
496             $string = substr($string,0,$length-4);
497             $pos = strrpos($string,".");
498         }
499         if ($pos>=$length*0.4) {
500             return substr($string,0,$pos+1)." ...";
501         }
502
503         $pos = strrpos($string," ");
504         if ($pos>=$length-4) {
505             $string = substr($string,0,$length-4);
506             $pos = strrpos($string," ");
507         }
508         if ($pos>=$length*0.4) {
509             return substr($string,0,$pos)." ...";
510         }
511
512         return substr($string,0,$length-4)." ...";
513
514     }
515
516
517     /**
518      * Creates a comment indicating the generator of this feed.
519      * The format of this comment seems to be recognized by
520      * Syndic8.com.
521      */
522     function _createGeneratorComment() {
523         return "<!-- generator=\"".FEEDCREATOR_VERSION."\" -->\n";
524     }
525
526
527     /**
528      * Creates a string containing all additional elements specified in
529      * $additionalElements.
530      * @param   elements    array   an associative array containing key => value pairs
531      * @param indentString  string  a string that will be inserted before every generated line
532      * @return    string    the XML tags corresponding to $additionalElements
533      */
534     function _createAdditionalElements($elements, $indentString="") {
535         $ae = "";
536         if (is_array($elements)) {
537             foreach($elements AS $key => $value) {
538                 $ae.= $indentString."<$key>$value</$key>\n";
539             }
540         }
541         return $ae;
542     }
543
544     function _createStylesheetReferences() {
545         $xml = "";
546         if ($this->cssStyleSheet) $xml .= "<?xml-stylesheet href=\"".$this->cssStyleSheet."\" type=\"text/css\"?>\n";
547         if ($this->xslStyleSheet) $xml .= "<?xml-stylesheet href=\"".$this->xslStyleSheet."\" type=\"text/xsl\"?>\n";
548         return $xml;
549     }
550
551
552     /**
553      * Builds the feed's text.
554      * @abstract
555      * @return    string    the feed's complete text
556      */
557     function createFeed() {
558     }
559
560     /**
561      * Generate a filename for the feed cache file. The result will be $_SERVER["PHP_SELF"] with the extension changed to .xml.
562      * For example:
563      *
564      * echo $_SERVER["PHP_SELF"]."\n";
565      * echo FeedCreator::_generateFilename();
566      *
567      * would produce:
568      *
569      * /rss/latestnews.php
570      * latestnews.xml
571      *
572      * @return string the feed cache filename
573      * @since 1.4
574      * @access private
575      */
576     function _generateFilename() {
577         $fileInfo = pathinfo($_SERVER["PHP_SELF"]);
578         return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".xml";
579     }
580
581
582     /**
583      * @since 1.4
584      * @access private
585      */
586     function _redirect($filename) {
587         // attention, heavily-commented-out-area
588
589         // maybe use this in addition to file time checking
590         //Header("Expires: ".date("r",time()+$this->_timeout));
591
592         /* no caching at all, doesn't seem to work as good:
593         Header("Cache-Control: no-cache");
594         Header("Pragma: no-cache");
595         */
596
597         // HTTP redirect, some feed readers' simple HTTP implementations don't follow it
598         //Header("Location: ".$filename);
599
600         header("Content-Type: ".$this->contentType."; charset=".$this->encoding."; filename=".utf8_basename($filename));
601         header("Content-Disposition: inline; filename=".utf8_basename($filename));
602         readfile($filename, "r");
603         die();
604     }
605
606     /**
607      * Turns on caching and checks if there is a recent version of this feed in the cache.
608      * If there is, an HTTP redirect header is sent.
609      * To effectively use caching, you should create the FeedCreator object and call this method
610      * before anything else, especially before you do the time consuming task to build the feed
611      * (web fetching, for example).
612      * @since 1.4
613      * @param filename  string  optional    the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
614      * @param timeout   int     optional    the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour)
615      */
616     function useCached($filename="", $timeout=3600) {
617         $this->_timeout = $timeout;
618         if ($filename=="") {
619             $filename = $this->_generateFilename();
620         }
621         if (file_exists($filename) AND (time()-filemtime($filename) < $timeout)) {
622             $this->_redirect($filename);
623         }
624     }
625
626
627     /**
628      * Saves this feed as a file on the local disk. After the file is saved, a redirect
629      * header may be sent to redirect the user to the newly created file.
630      * @since 1.4
631      *
632      * @param filename  string  optional    the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
633      * @param redirect  boolean optional    send an HTTP redirect header or not. If true, the user will be automatically redirected to the created file.
634      */
635     function saveFeed($filename="", $displayContents=true) {
636         if ($filename=="") {
637             $filename = $this->_generateFilename();
638         }
639         $feedFile = fopen($filename, "w+");
640         if ($feedFile) {
641             fputs($feedFile,$this->createFeed());
642             fclose($feedFile);
643             if ($displayContents) {
644                 $this->_redirect($filename);
645             }
646         } else {
647             echo "<br /><b>Error creating feed file, please check write permissions.</b><br />";
648         }
649     }
650
651     /**
652      * Outputs this feed directly to the browser - for on-the-fly feed generation
653      * @since 1.7.2-mod
654      *
655      * still missing: proper header output - currently you have to add it manually
656      */
657     function outputFeed() {
658         echo $this->createFeed();
659     }
660
661
662 }
663
664
665 /**
666  * FeedDate is an internal class that stores a date for a feed or feed item.
667  * Usually, you won't need to use this.
668  */
669 class FeedDate {
670     var $unix;
671
672     /**
673      * Creates a new instance of FeedDate representing a given date.
674      * Accepts RFC 822, ISO 8601 date formats as well as unix time stamps.
675      * @param mixed $dateString optional the date this FeedDate will represent. If not specified, the current date and time is used.
676      */
677     function FeedDate($dateString="") {
678         if ($dateString=="") $dateString = date("r");
679
680         if (is_numeric($dateString)) {
681             $this->unix = $dateString;
682             return;
683         }
684         if (preg_match("~(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?(\\d{1,2})\\s+([a-zA-Z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+(.*)~",$dateString,$matches)) {
685             $months = Array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12);
686             $this->unix = mktime($matches[4],$matches[5],$matches[6],$months[$matches[2]],$matches[1],$matches[3]);
687             if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') {
688                 $tzOffset = (((int) substr($matches[7],0,3) * 60) +
689                              (int) substr($matches[7],-2)) * 60;
690             } else {
691                 if (strlen($matches[7])==1) {
692                     $oneHour = 3600;
693                     $ord = ord($matches[7]);
694                     if ($ord < ord("M")) {
695                         $tzOffset = (ord("A") - $ord - 1) * $oneHour;
696                     } elseif ($ord >= ord("M") AND $matches[7]!="Z") {
697                         $tzOffset = ($ord - ord("M")) * $oneHour;
698                     } elseif ($matches[7]=="Z") {
699                         $tzOffset = 0;
700                     }
701                 }
702                 switch ($matches[7]) {
703                     case "UT":
704                     case "GMT": $tzOffset = 0;
705                 }
706             }
707             $this->unix += $tzOffset;
708             return;
709         }
710         if (preg_match("~(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(.*)~",$dateString,$matches)) {
711             $this->unix = mktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]);
712             if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') {
713                 $tzOffset = (((int) substr($matches[7],0,3) * 60) +
714                              (int) substr($matches[7],-2)) * 60;
715             } else {
716                 if ($matches[7]=="Z") {
717                     $tzOffset = 0;
718                 }
719             }
720             $this->unix += $tzOffset;
721             return;
722         }
723         $this->unix = 0;
724     }
725
726     /**
727      * Gets the date stored in this FeedDate as an RFC 822 date.
728      *
729      * @return a date in RFC 822 format
730      */
731     function rfc822() {
732         //return gmdate("r",$this->unix);
733         $date = gmdate("D, d M Y H:i:s", $this->unix);
734         if (TIME_ZONE!="") $date .= " ".str_replace(":","",TIME_ZONE);
735         return $date;
736     }
737
738     /**
739      * Gets the date stored in this FeedDate as an ISO 8601 date.
740      *
741      * @return a date in ISO 8601 (RFC 3339) format
742      */
743     function iso8601() {
744         $date = gmdate("Y-m-d\TH:i:sO",$this->unix);
745         if (TIME_ZONE!="") $date = str_replace("+0000",TIME_ZONE,$date);
746         $date = substr($date,0,22) . ':' . substr($date,-2);
747         return $date;
748     }
749
750
751     /**
752      * Gets the date stored in this FeedDate as unix time stamp.
753      *
754      * @return a date as a unix time stamp
755      */
756     function unix() {
757         return $this->unix;
758     }
759 }
760
761
762 /**
763  * RSSCreator10 is a FeedCreator that implements RDF Site Summary (RSS) 1.0.
764  *
765  * @see http://www.purl.org/rss/1.0/
766  * @since 1.3
767  * @author Kai Blankenhorn <kaib@bitfolge.de>
768  */
769 class RSSCreator10 extends FeedCreator {
770
771     /**
772      * Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0.
773      * The feed will contain all items previously added in the same order.
774      * @return    string    the feed's complete text
775      */
776     function createFeed() {
777         $feed = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
778         $feed.= $this->_createGeneratorComment();
779         if ($this->cssStyleSheet=="") {
780             $cssStyleSheet = "http://www.w3.org/2000/08/w3c-synd/style.css";
781         }
782         $feed.= $this->_createStylesheetReferences();
783         $feed.= "<rdf:RDF\n";
784         $feed.= "    xmlns=\"http://purl.org/rss/1.0/\"\n";
785         $feed.= "    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n";
786         $feed.= "    xmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n";
787         $feed.= "    xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n";
788         $feed.= "    <channel rdf:about=\"".$this->syndicationURL."\">\n";
789         $feed.= "        <title>".htmlspecialchars($this->title)."</title>\n";
790         $feed.= "        <description>".htmlspecialchars($this->description)."</description>\n";
791         $feed.= "        <link>".$this->link."</link>\n";
792         if ($this->image!=null) {
793             $feed.= "        <image rdf:resource=\"".$this->image->url."\" />\n";
794         }
795         $now = new FeedDate();
796         $feed.= "       <dc:date>".htmlspecialchars($now->iso8601())."</dc:date>\n";
797         $feed.= "        <items>\n";
798         $feed.= "            <rdf:Seq>\n";
799         $icnt = count($this->items);
800         for ($i=0; $i<$icnt; $i++) {
801             $feed.= "                <rdf:li rdf:resource=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
802         }
803         $feed.= "            </rdf:Seq>\n";
804         $feed.= "        </items>\n";
805         $feed.= "    </channel>\n";
806         if ($this->image!=null) {
807             $feed.= "    <image rdf:about=\"".$this->image->url."\">\n";
808             $feed.= "        <title>".htmlspecialchars($this->image->title)."</title>\n";
809             $feed.= "        <link>".$this->image->link."</link>\n";
810             $feed.= "        <url>".$this->image->url."</url>\n";
811             $feed.= "    </image>\n";
812         }
813         $feed.= $this->_createAdditionalElements($this->additionalElements, "    ");
814
815         $icnt = count($this->items);
816         for ($i=0; $i<$icnt; $i++) {
817             $feed.= "    <item rdf:about=\"".htmlspecialchars($this->items[$i]->link)."\">\n";
818             //$feed.= "        <dc:type>Posting</dc:type>\n";
819             $feed.= "        <dc:format>text/html</dc:format>\n";
820             if ($this->items[$i]->date!=null) {
821                 $itemDate = new FeedDate($this->items[$i]->date);
822                 $feed.= "        <dc:date>".htmlspecialchars($itemDate->iso8601())."</dc:date>\n";
823             }
824             if ($this->items[$i]->source!="") {
825                 $feed.= "        <dc:source>".htmlspecialchars($this->items[$i]->source)."</dc:source>\n";
826             }
827             if ($this->items[$i]->author!="") {
828                 $feed.= "        <dc:creator>".htmlspecialchars($this->items[$i]->author)."</dc:creator>\n";
829             }
830             $feed.= "        <title>".htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r","  ")))."</title>\n";
831             $feed.= "        <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
832             $feed.= "        <description>".htmlspecialchars($this->items[$i]->description)."</description>\n";
833             $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, "        ");
834             $feed.= "    </item>\n";
835         }
836         $feed.= "</rdf:RDF>\n";
837         return $feed;
838     }
839 }
840
841
842
843 /**
844  * RSSCreator091 is a FeedCreator that implements RSS 0.91 Spec, revision 3.
845  *
846  * @see http://my.netscape.com/publish/formats/rss-spec-0.91.html
847  * @since 1.3
848  * @author Kai Blankenhorn <kaib@bitfolge.de>
849  */
850 class RSSCreator091 extends FeedCreator {
851
852     /**
853      * Stores this RSS feed's version number.
854      * @access private
855      */
856     var $RSSVersion;
857
858     function RSSCreator091() {
859         $this->_setRSSVersion("0.91");
860         $this->contentType = "application/rss+xml";
861     }
862
863     /**
864      * Sets this RSS feed's version number.
865      * @access private
866      */
867     function _setRSSVersion($version) {
868         $this->RSSVersion = $version;
869     }
870
871     /**
872      * Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0.
873      * The feed will contain all items previously added in the same order.
874      * @return    string    the feed's complete text
875      */
876     function createFeed() {
877         $feed = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
878         $feed.= $this->_createGeneratorComment();
879         $feed.= $this->_createStylesheetReferences();
880         $feed.= "<rss version=\"".$this->RSSVersion."\">\n";
881         $feed.= "    <channel>\n";
882         $feed.= "        <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n";
883         $this->descriptionTruncSize = 500;
884         $feed.= "        <description>".$this->getDescription()."</description>\n";
885         $feed.= "        <link>".$this->link."</link>\n";
886         $now = new FeedDate();
887         $feed.= "        <lastBuildDate>".htmlspecialchars($now->rfc822())."</lastBuildDate>\n";
888         $feed.= "        <generator>".FEEDCREATOR_VERSION."</generator>\n";
889
890         if ($this->image!=null) {
891             $feed.= "        <image>\n";
892             $feed.= "            <url>".$this->image->url."</url>\n";
893             $feed.= "            <title>".FeedCreator::iTrunc(htmlspecialchars($this->image->title),100)."</title>\n";
894             $feed.= "            <link>".$this->image->link."</link>\n";
895             if ($this->image->width!="") {
896                 $feed.= "            <width>".$this->image->width."</width>\n";
897             }
898             if ($this->image->height!="") {
899                 $feed.= "            <height>".$this->image->height."</height>\n";
900             }
901             if ($this->image->description!="") {
902                 $feed.= "            <description>".$this->image->getDescription()."</description>\n";
903             }
904             $feed.= "        </image>\n";
905         }
906         if ($this->language!="") {
907             $feed.= "        <language>".$this->language."</language>\n";
908         }
909         if ($this->copyright!="") {
910             $feed.= "        <copyright>".FeedCreator::iTrunc(htmlspecialchars($this->copyright),100)."</copyright>\n";
911         }
912         if ($this->editor!="") {
913             $feed.= "        <managingEditor>".FeedCreator::iTrunc(htmlspecialchars($this->editor),100)."</managingEditor>\n";
914         }
915         if ($this->webmaster!="") {
916             $feed.= "        <webMaster>".FeedCreator::iTrunc(htmlspecialchars($this->webmaster),100)."</webMaster>\n";
917         }
918         if ($this->pubDate!="") {
919             $pubDate = new FeedDate($this->pubDate);
920             $feed.= "        <pubDate>".htmlspecialchars($pubDate->rfc822())."</pubDate>\n";
921         }
922         if ($this->category!="") {
923             // Changed for DokuWiki: multiple categories are possible
924             if(is_array($this->category)) foreach($this->category as $cat){
925                 $feed.= "        <category>".htmlspecialchars($cat)."</category>\n";
926             }else{
927                 $feed.= "        <category>".htmlspecialchars($this->category)."</category>\n";
928             }
929         }
930         if ($this->docs!="") {
931             $feed.= "        <docs>".FeedCreator::iTrunc(htmlspecialchars($this->docs),500)."</docs>\n";
932         }
933         if ($this->ttl!="") {
934             $feed.= "        <ttl>".htmlspecialchars($this->ttl)."</ttl>\n";
935         }
936         if ($this->rating!="") {
937             $feed.= "        <rating>".FeedCreator::iTrunc(htmlspecialchars($this->rating),500)."</rating>\n";
938         }
939         if ($this->skipHours!="") {
940             $feed.= "        <skipHours>".htmlspecialchars($this->skipHours)."</skipHours>\n";
941         }
942         if ($this->skipDays!="") {
943             $feed.= "        <skipDays>".htmlspecialchars($this->skipDays)."</skipDays>\n";
944         }
945         $feed.= $this->_createAdditionalElements($this->additionalElements, "    ");
946
947         $icnt = count($this->items);
948         for ($i=0; $i<$icnt; $i++) {
949             $feed.= "        <item>\n";
950             $feed.= "            <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n";
951             $feed.= "            <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
952             $feed.= "            <description>".$this->items[$i]->getDescription()."</description>\n";
953
954             if ($this->items[$i]->author!="") {
955                 $feed.= "            <author>".htmlspecialchars($this->items[$i]->author)."</author>\n";
956             }
957             /*
958             // on hold
959             if ($this->items[$i]->source!="") {
960                     $feed.= "            <source>".htmlspecialchars($this->items[$i]->source)."</source>\n";
961             }
962             */
963             if ($this->items[$i]->category!="") {
964                 // Changed for DokuWiki: multiple categories are possible
965                 if(is_array($this->items[$i]->category)) foreach($this->items[$i]->category as $cat){
966                     $feed.= "        <category>".htmlspecialchars($cat)."</category>\n";
967                 }else{
968                     $feed.= "        <category>".htmlspecialchars($this->items[$i]->category)."</category>\n";
969                 }
970             }
971
972             if ($this->items[$i]->comments!="") {
973                 $feed.= "            <comments>".htmlspecialchars($this->items[$i]->comments)."</comments>\n";
974             }
975             if ($this->items[$i]->date!="") {
976                 $itemDate = new FeedDate($this->items[$i]->date);
977                 $feed.= "            <pubDate>".htmlspecialchars($itemDate->rfc822())."</pubDate>\n";
978             }
979             if ($this->items[$i]->guid!="") {
980                 $feed.= "            <guid>".htmlspecialchars($this->items[$i]->guid)."</guid>\n";
981             }
982             $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, "        ");
983
984             if ($this->RSSVersion == "2.0" && $this->items[$i]->enclosure != null) {
985                  $feed.= "            <enclosure url=\"";
986                  $feed.= $this->items[$i]->enclosure->url;
987                  $feed.= "\" length=\"";
988                  $feed.= $this->items[$i]->enclosure->length;
989                  $feed.= "\" type=\"";
990                  $feed.= $this->items[$i]->enclosure->type;
991                  $feed.= "\"/>\n";
992             }
993
994             $feed.= "        </item>\n";
995         }
996
997         $feed.= "    </channel>\n";
998         $feed.= "</rss>\n";
999         return $feed;
1000     }
1001 }
1002
1003
1004
1005 /**
1006  * RSSCreator20 is a FeedCreator that implements RDF Site Summary (RSS) 2.0.
1007  *
1008  * @see http://backend.userland.com/rss
1009  * @since 1.3
1010  * @author Kai Blankenhorn <kaib@bitfolge.de>
1011  */
1012 class RSSCreator20 extends RSSCreator091 {
1013
1014     function RSSCreator20() {
1015         parent::_setRSSVersion("2.0");
1016     }
1017
1018 }
1019
1020
1021 /**
1022  * PIECreator01 is a FeedCreator that implements the emerging PIE specification,
1023  * as in http://intertwingly.net/wiki/pie/Syntax.
1024  *
1025  * @deprecated
1026  * @since 1.3
1027  * @author Scott Reynen <scott@randomchaos.com> and Kai Blankenhorn <kaib@bitfolge.de>
1028  */
1029 class PIECreator01 extends FeedCreator {
1030
1031     function PIECreator01() {
1032         $this->encoding = "utf-8";
1033     }
1034
1035     function createFeed() {
1036         $feed = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
1037         $feed.= $this->_createStylesheetReferences();
1038         $feed.= "<feed version=\"0.1\" xmlns=\"http://example.com/newformat#\">\n";
1039         $feed.= "    <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n";
1040         $this->truncSize = 500;
1041         $feed.= "    <subtitle>".$this->getDescription()."</subtitle>\n";
1042         $feed.= "    <link>".$this->link."</link>\n";
1043         $icnt = count($this->items);
1044         for ($i=0; $i<$icnt; $i++) {
1045             $feed.= "    <entry>\n";
1046             $feed.= "        <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n";
1047             $feed.= "        <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
1048             $itemDate = new FeedDate($this->items[$i]->date);
1049             $feed.= "        <created>".htmlspecialchars($itemDate->iso8601())."</created>\n";
1050             $feed.= "        <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n";
1051             $feed.= "        <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n";
1052             $feed.= "        <id>".htmlspecialchars($this->items[$i]->guid)."</id>\n";
1053             if ($this->items[$i]->author!="") {
1054                 $feed.= "        <author>\n";
1055                 $feed.= "            <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
1056                 if ($this->items[$i]->authorEmail!="") {
1057                     $feed.= "            <email>".$this->items[$i]->authorEmail."</email>\n";
1058                 }
1059                 $feed.="        </author>\n";
1060             }
1061             $feed.= "        <content type=\"text/html\" xml:lang=\"en-us\">\n";
1062             $feed.= "            <div xmlns=\"http://www.w3.org/1999/xhtml\">".$this->items[$i]->getDescription()."</div>\n";
1063             $feed.= "        </content>\n";
1064             $feed.= "    </entry>\n";
1065         }
1066         $feed.= "</feed>\n";
1067         return $feed;
1068     }
1069 }
1070
1071 /**
1072  * AtomCreator10 is a FeedCreator that implements the atom specification,
1073  * as in http://www.atomenabled.org/developers/syndication/atom-format-spec.php
1074  * Please note that just by using AtomCreator10 you won't automatically
1075  * produce valid atom files. For example, you have to specify either an editor
1076  * for the feed or an author for every single feed item.
1077  *
1078  * Some elements have not been implemented yet. These are (incomplete list):
1079  * author URL, item author's email and URL, item contents, alternate links,
1080  * other link content types than text/html. Some of them may be created with
1081  * AtomCreator10::additionalElements.
1082  *
1083  * @see FeedCreator#additionalElements
1084  * @since 1.7.2-mod (modified)
1085  * @author Mohammad Hafiz Ismail (mypapit@gmail.com)
1086  */
1087 class AtomCreator10 extends FeedCreator {
1088
1089     function AtomCreator10() {
1090         $this->contentType = "application/atom+xml";
1091         $this->encoding = "utf-8";
1092     }
1093
1094     function createFeed() {
1095         $feed = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
1096         $feed.= $this->_createGeneratorComment();
1097         $feed.= $this->_createStylesheetReferences();
1098         $feed.= "<feed xmlns=\"http://www.w3.org/2005/Atom\"";
1099         if ($this->language!="") {
1100             $feed.= " xml:lang=\"".$this->language."\"";
1101         }
1102         $feed.= ">\n";
1103         $feed.= "    <title>".htmlspecialchars($this->title)."</title>\n";
1104         $feed.= "    <subtitle>".htmlspecialchars($this->description)."</subtitle>\n";
1105         $feed.= "    <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->link)."\"/>\n";
1106         $feed.= "    <id>".htmlspecialchars($this->link)."</id>\n";
1107         $now = new FeedDate();
1108         $feed.= "    <updated>".htmlspecialchars($now->iso8601())."</updated>\n";
1109         if ($this->editor!="") {
1110             $feed.= "    <author>\n";
1111             $feed.= "        <name>".$this->editor."</name>\n";
1112             if ($this->editorEmail!="") {
1113                 $feed.= "        <email>".$this->editorEmail."</email>\n";
1114             }
1115             $feed.= "    </author>\n";
1116         }
1117         $feed.= "    <generator>".FEEDCREATOR_VERSION."</generator>\n";
1118         $feed.= "<link rel=\"self\" type=\"application/atom+xml\" href=\"". $this->syndicationURL . "\" />\n";
1119         $feed.= $this->_createAdditionalElements($this->additionalElements, "    ");
1120         $icnt = count($this->items);
1121         for ($i=0; $i<$icnt; $i++) {
1122             $feed.= "    <entry>\n";
1123             $feed.= "        <title>".htmlspecialchars(strip_tags($this->items[$i]->title))."</title>\n";
1124             $feed.= "        <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
1125             if ($this->items[$i]->date=="") {
1126                 $this->items[$i]->date = time();
1127             }
1128             $itemDate = new FeedDate($this->items[$i]->date);
1129             $feed.= "        <published>".htmlspecialchars($itemDate->iso8601())."</published>\n";
1130             $feed.= "        <updated>".htmlspecialchars($itemDate->iso8601())."</updated>\n";
1131             $feed.= "        <id>".htmlspecialchars($this->items[$i]->link)."</id>\n";
1132             $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, "        ");
1133             if ($this->items[$i]->author!="") {
1134                 $feed.= "        <author>\n";
1135                 $feed.= "            <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
1136                 $feed.= "        </author>\n";
1137             }
1138             if ($this->items[$i]->description!="") {
1139                 $feed.= "        <summary>".htmlspecialchars($this->items[$i]->description)."</summary>\n";
1140             }
1141             if ($this->items[$i]->enclosure != null) {
1142                 $feed.="        <link rel=\"enclosure\" href=\"". $this->items[$i]->enclosure->url ."\" type=\"". $this->items[$i]->enclosure->type."\"  length=\"". $this->items[$i]->enclosure->length . "\" />\n";
1143             }
1144             $feed.= "    </entry>\n";
1145         }
1146         $feed.= "</feed>\n";
1147         return $feed;
1148     }
1149
1150
1151 }
1152
1153
1154 /**
1155  * AtomCreator03 is a FeedCreator that implements the atom specification,
1156  * as in http://www.intertwingly.net/wiki/pie/FrontPage.
1157  * Please note that just by using AtomCreator03 you won't automatically
1158  * produce valid atom files. For example, you have to specify either an editor
1159  * for the feed or an author for every single feed item.
1160  *
1161  * Some elements have not been implemented yet. These are (incomplete list):
1162  * author URL, item author's email and URL, item contents, alternate links,
1163  * other link content types than text/html. Some of them may be created with
1164  * AtomCreator03::additionalElements.
1165  *
1166  * @see FeedCreator#additionalElements
1167  * @since 1.6
1168  * @author Kai Blankenhorn <kaib@bitfolge.de>, Scott Reynen <scott@randomchaos.com>
1169  */
1170 class AtomCreator03 extends FeedCreator {
1171
1172     function AtomCreator03() {
1173         $this->contentType = "application/atom+xml";
1174         $this->encoding = "utf-8";
1175     }
1176
1177     function createFeed() {
1178         $feed = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
1179         $feed.= $this->_createGeneratorComment();
1180         $feed.= $this->_createStylesheetReferences();
1181         $feed.= "<feed version=\"0.3\" xmlns=\"http://purl.org/atom/ns#\"";
1182         if ($this->language!="") {
1183             $feed.= " xml:lang=\"".$this->language."\"";
1184         }
1185         $feed.= ">\n";
1186         $feed.= "    <title>".htmlspecialchars($this->title)."</title>\n";
1187         $feed.= "    <tagline>".htmlspecialchars($this->description)."</tagline>\n";
1188         $feed.= "    <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->link)."\"/>\n";
1189         $feed.= "    <id>".htmlspecialchars($this->link)."</id>\n";
1190         $now = new FeedDate();
1191         $feed.= "    <modified>".htmlspecialchars($now->iso8601())."</modified>\n";
1192         if ($this->editor!="") {
1193             $feed.= "    <author>\n";
1194             $feed.= "        <name>".$this->editor."</name>\n";
1195             if ($this->editorEmail!="") {
1196                 $feed.= "        <email>".$this->editorEmail."</email>\n";
1197             }
1198             $feed.= "    </author>\n";
1199         }
1200         $feed.= "    <generator>".FEEDCREATOR_VERSION."</generator>\n";
1201         $feed.= $this->_createAdditionalElements($this->additionalElements, "    ");
1202         $icnt = count($this->items);
1203         for ($i=0; $i<$icnt; $i++) {
1204             $feed.= "    <entry>\n";
1205             $feed.= "        <title>".htmlspecialchars(strip_tags($this->items[$i]->title))."</title>\n";
1206             $feed.= "        <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
1207             if ($this->items[$i]->date=="") {
1208                 $this->items[$i]->date = time();
1209             }
1210             $itemDate = new FeedDate($this->items[$i]->date);
1211             $feed.= "        <created>".htmlspecialchars($itemDate->iso8601())."</created>\n";
1212             $feed.= "        <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n";
1213             $feed.= "        <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n";
1214             $feed.= "        <id>".htmlspecialchars($this->items[$i]->link)."</id>\n";
1215             $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, "        ");
1216             if ($this->items[$i]->author!="") {
1217                 $feed.= "        <author>\n";
1218                 $feed.= "            <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
1219                 $feed.= "        </author>\n";
1220             }
1221             if ($this->items[$i]->description!="") {
1222                 $feed.= "        <summary>".htmlspecialchars($this->items[$i]->description)."</summary>\n";
1223             }
1224             $feed.= "    </entry>\n";
1225         }
1226         $feed.= "</feed>\n";
1227         return $feed;
1228     }
1229 }
1230
1231
1232 /**
1233  * MBOXCreator is a FeedCreator that implements the mbox format
1234  * as described in http://www.qmail.org/man/man5/mbox.html
1235  *
1236  * @since 1.3
1237  * @author Kai Blankenhorn <kaib@bitfolge.de>
1238  */
1239 class MBOXCreator extends FeedCreator {
1240
1241     function MBOXCreator() {
1242         $this->contentType = "text/plain";
1243         $this->encoding = "utf-8";
1244     }
1245
1246     function qp_enc($input = "", $line_max = 76) {
1247         $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
1248         $lines = preg_split("/(?:\r\n|\r|\n)/", $input);
1249         $eol = "\r\n";
1250         $escape = "=";
1251         $output = "";
1252         while( list(, $line) = each($lines) ) {
1253             //$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
1254             $linlen = strlen($line);
1255             $newline = "";
1256             for($i = 0; $i < $linlen; $i++) {
1257                 $c = substr($line, $i, 1);
1258                 $dec = ord($c);
1259                 if ( ($dec == 32) && ($i == ($linlen - 1)) ) { // convert space at eol only
1260                     $c = "=20";
1261                 } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
1262                     $h2 = floor($dec/16);
1263                     $h1 = floor($dec%16);
1264                     $c = $escape.$hex["$h2"].$hex["$h1"];
1265                 }
1266                 if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
1267                     $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
1268                     $newline = "";
1269                 }
1270                 $newline .= $c;
1271             } // end of for
1272             $output .= $newline.$eol;
1273         }
1274         return trim($output);
1275     }
1276
1277
1278     /**
1279      * Builds the MBOX contents.
1280      * @return    string    the feed's complete text
1281      */
1282     function createFeed() {
1283         $icnt = count($this->items);
1284         for ($i=0; $i<$icnt; $i++) {
1285             if ($this->items[$i]->author!="") {
1286                 $from = $this->items[$i]->author;
1287             } else {
1288                 $from = $this->title;
1289             }
1290             $itemDate = new FeedDate($this->items[$i]->date);
1291             $feed.= "From ".strtr(MBOXCreator::qp_enc($from)," ","_")." ".date("D M d H:i:s Y",$itemDate->unix())."\n";
1292             $feed.= "Content-Type: text/plain;\n";
1293             $feed.= "   charset=\"".$this->encoding."\"\n";
1294             $feed.= "Content-Transfer-Encoding: quoted-printable\n";
1295             $feed.= "Content-Type: text/plain\n";
1296             $feed.= "From: \"".MBOXCreator::qp_enc($from)."\"\n";
1297             $feed.= "Date: ".$itemDate->rfc822()."\n";
1298             $feed.= "Subject: ".MBOXCreator::qp_enc(FeedCreator::iTrunc($this->items[$i]->title,100))."\n";
1299             $feed.= "\n";
1300             $body = chunk_split(MBOXCreator::qp_enc($this->items[$i]->description));
1301             $feed.= preg_replace("~\nFrom ([^\n]*)(\n?)~","\n>From $1$2\n",$body);
1302             $feed.= "\n";
1303             $feed.= "\n";
1304         }
1305         return $feed;
1306     }
1307
1308     /**
1309      * Generate a filename for the feed cache file. Overridden from FeedCreator to prevent XML data types.
1310      * @return string the feed cache filename
1311      * @since 1.4
1312      * @access private
1313      */
1314     function _generateFilename() {
1315         $fileInfo = pathinfo($_SERVER["PHP_SELF"]);
1316         return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".mbox";
1317     }
1318 }
1319
1320
1321 /**
1322  * OPMLCreator is a FeedCreator that implements OPML 1.0.
1323  *
1324  * @see http://opml.scripting.com/spec
1325  * @author Dirk Clemens, Kai Blankenhorn
1326  * @since 1.5
1327  */
1328 class OPMLCreator extends FeedCreator {
1329
1330     function OPMLCreator() {
1331         $this->encoding = "utf-8";
1332     }
1333
1334     function createFeed() {
1335         $feed = "<?xml version=\"1.0\" encoding=\"".$this->encoding."\"?>\n";
1336         $feed.= $this->_createGeneratorComment();
1337         $feed.= $this->_createStylesheetReferences();
1338         $feed.= "<opml xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
1339         $feed.= "    <head>\n";
1340         $feed.= "        <title>".htmlspecialchars($this->title)."</title>\n";
1341         if ($this->pubDate!="") {
1342             $date = new FeedDate($this->pubDate);
1343             $feed.= "         <dateCreated>".$date->rfc822()."</dateCreated>\n";
1344         }
1345         if ($this->lastBuildDate!="") {
1346             $date = new FeedDate($this->lastBuildDate);
1347             $feed.= "         <dateModified>".$date->rfc822()."</dateModified>\n";
1348         }
1349         if ($this->editor!="") {
1350             $feed.= "         <ownerName>".$this->editor."</ownerName>\n";
1351         }
1352         if ($this->editorEmail!="") {
1353             $feed.= "         <ownerEmail>".$this->editorEmail."</ownerEmail>\n";
1354         }
1355         $feed.= "    </head>\n";
1356         $feed.= "    <body>\n";
1357         $icnt = count($this->items);
1358         for ($i=0;$i<$icnt; $i++) {
1359             $feed.= "    <outline type=\"rss\" ";
1360             $title = htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r","  ")));
1361             $feed.= " title=\"".$title."\"";
1362             $feed.= " text=\"".$title."\"";
1363             //$feed.= " description=\"".htmlspecialchars($this->items[$i]->description)."\"";
1364             $feed.= " url=\"".htmlspecialchars($this->items[$i]->link)."\"";
1365             $feed.= "/>\n";
1366         }
1367         $feed.= "    </body>\n";
1368         $feed.= "</opml>\n";
1369         return $feed;
1370     }
1371 }
1372
1373
1374
1375 /**
1376  * HTMLCreator is a FeedCreator that writes an HTML feed file to a specific
1377  * location, overriding the createFeed method of the parent FeedCreator.
1378  * The HTML produced can be included over http by scripting languages, or serve
1379  * as the source for an IFrame.
1380  * All output by this class is embedded in <div></div> tags to enable formatting
1381  * using CSS.
1382  *
1383  * @author Pascal Van Hecke
1384  * @since 1.7
1385  */
1386 class HTMLCreator extends FeedCreator {
1387
1388     var $contentType = "text/html";
1389
1390     /**
1391      * Contains HTML to be output at the start of the feed's html representation.
1392      */
1393     var $header;
1394
1395     /**
1396      * Contains HTML to be output at the end of the feed's html representation.
1397      */
1398     var $footer ;
1399
1400     /**
1401      * Contains HTML to be output between entries. A separator is only used in
1402      * case of multiple entries.
1403      */
1404     var $separator;
1405
1406     /**
1407      * Used to prefix the stylenames to make sure they are unique
1408      * and do not clash with stylenames on the users' page.
1409      */
1410     var $stylePrefix;
1411
1412     /**
1413      * Determines whether the links open in a new window or not.
1414      */
1415     var $openInNewWindow = true;
1416
1417     var $imageAlign ="right";
1418
1419     /**
1420      * In case of very simple output you may want to get rid of the style tags,
1421      * hence this variable.  There's no equivalent on item level, but of course you can
1422      * add strings to it while iterating over the items ($this->stylelessOutput .= ...)
1423      * and when it is non-empty, ONLY the styleless output is printed, the rest is ignored
1424      * in the function createFeed().
1425      */
1426     var $stylelessOutput ="";
1427
1428     /**
1429      * Writes the HTML.
1430      * @return    string    the scripts's complete text
1431      */
1432     function createFeed() {
1433         // if there is styleless output, use the content of this variable and ignore the rest
1434         if ($this->stylelessOutput!="") {
1435             return $this->stylelessOutput;
1436         }
1437
1438         //if no stylePrefix is set, generate it yourself depending on the script name
1439         if ($this->stylePrefix=="") {
1440             $this->stylePrefix = str_replace(".", "_", $this->_generateFilename())."_";
1441         }
1442
1443         //set an openInNewWindow_token_to be inserted or not
1444         if ($this->openInNewWindow) {
1445             $targetInsert = " target='_blank'";
1446         }
1447
1448         // use this array to put the lines in and implode later with "document.write" javascript
1449         $feedArray = array();
1450         if ($this->image!=null) {
1451             $imageStr = "<a href='".$this->image->link."'".$targetInsert.">".
1452                             "<img src='".$this->image->url."' border='0' alt='".
1453                             FeedCreator::iTrunc(htmlspecialchars($this->image->title),100).
1454                             "' align='".$this->imageAlign."' ";
1455             if ($this->image->width) {
1456                 $imageStr .=" width='".$this->image->width. "' ";
1457             }
1458             if ($this->image->height) {
1459                 $imageStr .=" height='".$this->image->height."' ";
1460             }
1461             $imageStr .="/></a>";
1462             $feedArray[] = $imageStr;
1463         }
1464
1465         if ($this->title) {
1466             $feedArray[] = "<div class='".$this->stylePrefix."title'><a href='".$this->link."' ".$targetInsert." class='".$this->stylePrefix."title'>".
1467                 FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</a></div>";
1468         }
1469         if ($this->getDescription()) {
1470             $feedArray[] = "<div class='".$this->stylePrefix."description'>".
1471                 str_replace("]]>", "", str_replace("<![CDATA[", "", $this->getDescription())).
1472                 "</div>";
1473         }
1474
1475         if ($this->header) {
1476             $feedArray[] = "<div class='".$this->stylePrefix."header'>".$this->header."</div>";
1477         }
1478
1479         $icnt = count($this->items);
1480         for ($i=0; $i<$icnt; $i++) {
1481             if ($this->separator and $i > 0) {
1482                 $feedArray[] = "<div class='".$this->stylePrefix."separator'>".$this->separator."</div>";
1483             }
1484
1485             if ($this->items[$i]->title) {
1486                 if ($this->items[$i]->link) {
1487                     $feedArray[] =
1488                         "<div class='".$this->stylePrefix."item_title'><a href='".$this->items[$i]->link."' class='".$this->stylePrefix.
1489                         "item_title'".$targetInsert.">".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100).
1490                         "</a></div>";
1491                 } else {
1492                     $feedArray[] =
1493                         "<div class='".$this->stylePrefix."item_title'>".
1494                         FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100).
1495                         "</div>";
1496                 }
1497             }
1498             if ($this->items[$i]->getDescription()) {
1499                 $feedArray[] =
1500                 "<div class='".$this->stylePrefix."item_description'>".
1501                     str_replace("]]>", "", str_replace("<![CDATA[", "", $this->items[$i]->getDescription())).
1502                     "</div>";
1503             }
1504         }
1505         if ($this->footer) {
1506             $feedArray[] = "<div class='".$this->stylePrefix."footer'>".$this->footer."</div>";
1507         }
1508
1509         $feed= "".join($feedArray, "\r\n");
1510         return $feed;
1511     }
1512
1513     /**
1514      * Overrrides parent to produce .html extensions
1515      *
1516      * @return string the feed cache filename
1517      * @since 1.4
1518      * @access private
1519      */
1520     function _generateFilename() {
1521         $fileInfo = pathinfo($_SERVER["PHP_SELF"]);
1522         return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".html";
1523     }
1524 }
1525
1526
1527 /**
1528  * JSCreator is a class that writes a js file to a specific
1529  * location, overriding the createFeed method of the parent HTMLCreator.
1530  *
1531  * @author Pascal Van Hecke
1532  */
1533 class JSCreator extends HTMLCreator {
1534     var $contentType = "text/javascript";
1535
1536     /**
1537      * writes the javascript
1538      * @return    string    the scripts's complete text
1539      */
1540     function createFeed() {
1541         $feed = parent::createFeed();
1542         $feedArray = explode("\n",$feed);
1543
1544         $jsFeed = "";
1545         foreach ($feedArray as $value) {
1546             $jsFeed .= "document.write('".trim(addslashes($value))."');\n";
1547         }
1548         return $jsFeed;
1549     }
1550
1551     /**
1552      * Overrrides parent to produce .js extensions
1553      *
1554      * @return string the feed cache filename
1555      * @since 1.4
1556      * @access private
1557      */
1558     function _generateFilename() {
1559         $fileInfo = pathinfo($_SERVER["PHP_SELF"]);
1560         return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".js";
1561     }
1562
1563 }
1564
1565 /**
1566  * This class allows to override the hardcoded charset
1567  *
1568  * @author Andreas Gohr <andi@splitbrain.org>
1569  */
1570 class DokuWikiFeedCreator extends UniversalFeedCreator{
1571     function createFeed($format = "RSS0.91",$encoding='iso-8859-15') {
1572         $this->_setFormat($format);
1573         $this->_feed->encoding = $encoding;
1574         return $this->_feed->createFeed();
1575     }
1576 }
1577
1578
1579
1580 //Setup VIM: ex: et ts=4 :