[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> feedcreator.class.php (source)

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