source: trunk/include/feedcreator.class.php @ 5025

Last change on this file since 5025 was 5014, checked in by plg, 15 years ago

merge r5013 from branch 2.0 to trunk

feature 1448 added: ability to set the upload directory (for pwg.images.add
API method).

Warning: due to risk on img src construction, the upload_dir must be relative
to the Piwigo directory itself.

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