1572dd708SAndreas Gohr<?php 2572dd708SAndreas Gohr 3572dd708SAndreas Gohr/** 4572dd708SAndreas Gohr * FeedCreator is the abstract base implementation for concrete 5572dd708SAndreas Gohr * implementations that implement a specific format of syndication. 6572dd708SAndreas Gohr * 7572dd708SAndreas Gohr * @author Kai Blankenhorn <kaib@bitfolge.de> 8572dd708SAndreas Gohr * @since 1.4 9572dd708SAndreas Gohr */ 10572dd708SAndreas Gohrabstract class FeedCreator extends HtmlDescribable 11572dd708SAndreas Gohr{ 12572dd708SAndreas Gohr 13572dd708SAndreas Gohr /** 14572dd708SAndreas Gohr * Mandatory attributes of a feed. 15572dd708SAndreas Gohr */ 16572dd708SAndreas Gohr public $title, $description, $link; 17572dd708SAndreas Gohr public $format = 'BASE'; 18572dd708SAndreas Gohr 19572dd708SAndreas Gohr /** 20572dd708SAndreas Gohr * Optional attributes of a feed. 21572dd708SAndreas Gohr */ 22572dd708SAndreas Gohr public $syndicationURL, $image, $language, $copyright, $pubDate, $lastBuildDate, $editor, $editorEmail, $webmaster, $category, $docs, $ttl, $rating, $skipHours, $skipDays; 23572dd708SAndreas Gohr 24572dd708SAndreas Gohr /** 25572dd708SAndreas Gohr * The url of the external xsl stylesheet used to format the naked rss feed. 26572dd708SAndreas Gohr * Ignored in the output when empty. 27572dd708SAndreas Gohr */ 28572dd708SAndreas Gohr public $xslStyleSheet = ""; 29572dd708SAndreas Gohr 3064d8abdbSAndreas Gohr public $cssStyleSheet = ""; 31572dd708SAndreas Gohr 32572dd708SAndreas Gohr /** @var FeedItem[] */ 33572dd708SAndreas Gohr public $items = Array(); 34572dd708SAndreas Gohr 35572dd708SAndreas Gohr /** 36572dd708SAndreas Gohr * Generator string 37572dd708SAndreas Gohr */ 38572dd708SAndreas Gohr public $generator = "info@mypapit.net"; 39572dd708SAndreas Gohr 40572dd708SAndreas Gohr /** 41572dd708SAndreas Gohr * This feed's MIME content type. 42572dd708SAndreas Gohr * 43572dd708SAndreas Gohr * @since 1.4 44572dd708SAndreas Gohr * @access private 45572dd708SAndreas Gohr */ 46572dd708SAndreas Gohr protected $contentType = "application/xml"; 47572dd708SAndreas Gohr 48572dd708SAndreas Gohr /** 49572dd708SAndreas Gohr * This feed's character encoding. 50572dd708SAndreas Gohr * 51572dd708SAndreas Gohr * @since 1.6.1 52572dd708SAndreas Gohr */ 53572dd708SAndreas Gohr protected $encoding = "UTF-8"; //"ISO-8859-1"; 54572dd708SAndreas Gohr 55*2cadabe7SAndreas Gohr protected $_timeout; # lib/Creator/FeedCreator.php line 238 56*2cadabe7SAndreas Gohr 57572dd708SAndreas Gohr /** 58572dd708SAndreas Gohr * Any additional elements to include as an associated array. All $key => $value pairs 59572dd708SAndreas Gohr * will be included unencoded in the feed in the form 60572dd708SAndreas Gohr * <$key>$value</$key> 61572dd708SAndreas Gohr * Again: No encoding will be used! This means you can invalidate or enhance the feed 62572dd708SAndreas Gohr * if $value contains markup. This may be abused to embed tags not implemented by 63572dd708SAndreas Gohr * the FeedCreator class used. 64572dd708SAndreas Gohr */ 65572dd708SAndreas Gohr public $additionalElements = Array(); 66572dd708SAndreas Gohr 67572dd708SAndreas Gohr /** 68572dd708SAndreas Gohr * Adds a FeedItem to the feed. 69572dd708SAndreas Gohr * 70572dd708SAndreas Gohr * @param FeedItem $item The FeedItem to add to the feed. 71572dd708SAndreas Gohr */ 72572dd708SAndreas Gohr public function addItem($item) 73572dd708SAndreas Gohr { 74572dd708SAndreas Gohr $this->items[] = $item; 75572dd708SAndreas Gohr } 76572dd708SAndreas Gohr 77572dd708SAndreas Gohr /** 78572dd708SAndreas Gohr * Get the version string for the generator 79572dd708SAndreas Gohr * 80572dd708SAndreas Gohr * @return string 81572dd708SAndreas Gohr */ 82572dd708SAndreas Gohr public function version() 83572dd708SAndreas Gohr { 84572dd708SAndreas Gohr return FEEDCREATOR_VERSION." (".$this->generator.")"; 85572dd708SAndreas Gohr } 86572dd708SAndreas Gohr 87572dd708SAndreas Gohr /** 88572dd708SAndreas Gohr * Truncates a string to a certain length at the most sensible point. 89572dd708SAndreas Gohr * First, if there's a '.' character near the end of the string, the string is truncated after this character. 90572dd708SAndreas Gohr * If there is no '.', the string is truncated after the last ' ' character. 91572dd708SAndreas Gohr * If the string is truncated, " ..." is appended. 92572dd708SAndreas Gohr * If the string is already shorter than $length, it is returned unchanged. 93572dd708SAndreas Gohr * 94572dd708SAndreas Gohr * @param string $string A string to be truncated. 95572dd708SAndreas Gohr * @param int $length the maximum length the string should be truncated to 96572dd708SAndreas Gohr * @return string the truncated string 97572dd708SAndreas Gohr */ 98572dd708SAndreas Gohr public static function iTrunc($string, $length) 99572dd708SAndreas Gohr { 100572dd708SAndreas Gohr if (strlen($string) <= $length) { 101572dd708SAndreas Gohr return $string; 102572dd708SAndreas Gohr } 103572dd708SAndreas Gohr 104572dd708SAndreas Gohr $pos = strrpos($string, "."); 105572dd708SAndreas Gohr if ($pos >= $length - 4) { 106572dd708SAndreas Gohr $string = substr($string, 0, $length - 4); 107572dd708SAndreas Gohr $pos = strrpos($string, "."); 108572dd708SAndreas Gohr } 109572dd708SAndreas Gohr if ($pos >= $length * 0.4) { 110572dd708SAndreas Gohr return substr($string, 0, $pos + 1)." ..."; 111572dd708SAndreas Gohr } 112572dd708SAndreas Gohr 113572dd708SAndreas Gohr $pos = strrpos($string, " "); 114572dd708SAndreas Gohr if ($pos >= $length - 4) { 115572dd708SAndreas Gohr $string = substr($string, 0, $length - 4); 116572dd708SAndreas Gohr $pos = strrpos($string, " "); 117572dd708SAndreas Gohr } 118572dd708SAndreas Gohr if ($pos >= $length * 0.4) { 119572dd708SAndreas Gohr return substr($string, 0, $pos)." ..."; 120572dd708SAndreas Gohr } 121572dd708SAndreas Gohr 122572dd708SAndreas Gohr return substr($string, 0, $length - 4)." ..."; 123572dd708SAndreas Gohr 124572dd708SAndreas Gohr } 125572dd708SAndreas Gohr 126572dd708SAndreas Gohr /** 127572dd708SAndreas Gohr * Creates a comment indicating the generator of this feed. 128572dd708SAndreas Gohr * The format of this comment seems to be recognized by 129572dd708SAndreas Gohr * Syndic8.com. 130572dd708SAndreas Gohr */ 131572dd708SAndreas Gohr protected function _createGeneratorComment() 132572dd708SAndreas Gohr { 133572dd708SAndreas Gohr return "<!-- generator=\"".FEEDCREATOR_VERSION."\" -->\n"; 134572dd708SAndreas Gohr } 135572dd708SAndreas Gohr 136572dd708SAndreas Gohr /** 137572dd708SAndreas Gohr * Creates a string containing all additional elements specified in 138572dd708SAndreas Gohr * $additionalElements. 139572dd708SAndreas Gohr * 140572dd708SAndreas Gohr * @param array $elements an associative array containing key => value pairs 141572dd708SAndreas Gohr * @param string $indentString a string that will be inserted before every generated line 142572dd708SAndreas Gohr * @return string the XML tags corresponding to $additionalElements 143572dd708SAndreas Gohr */ 144572dd708SAndreas Gohr protected function _createAdditionalElements($elements, $indentString = "") 145572dd708SAndreas Gohr { 146572dd708SAndreas Gohr $ae = ""; 147572dd708SAndreas Gohr if (is_array($elements)) { 148572dd708SAndreas Gohr foreach ($elements AS $key => $value) { 149572dd708SAndreas Gohr $ae .= $indentString."<$key>$value</$key>\n"; 150572dd708SAndreas Gohr } 151572dd708SAndreas Gohr } 152572dd708SAndreas Gohr 153572dd708SAndreas Gohr return $ae; 154572dd708SAndreas Gohr } 155572dd708SAndreas Gohr 156572dd708SAndreas Gohr protected function _createStylesheetReferences() 157572dd708SAndreas Gohr { 158572dd708SAndreas Gohr $xml = ""; 159572dd708SAndreas Gohr if (!empty($this->cssStyleSheet)) { 160572dd708SAndreas Gohr $xml .= "<?xml-stylesheet href=\"".$this->cssStyleSheet."\" type=\"text/css\"?>\n"; 161572dd708SAndreas Gohr } 162572dd708SAndreas Gohr if (!empty($this->xslStyleSheet)) { 163572dd708SAndreas Gohr $xml .= "<?xml-stylesheet href=\"".$this->xslStyleSheet."\" type=\"text/xsl\"?>\n"; 164572dd708SAndreas Gohr } 165572dd708SAndreas Gohr 166572dd708SAndreas Gohr return $xml; 167572dd708SAndreas Gohr } 168572dd708SAndreas Gohr 169572dd708SAndreas Gohr /** 170572dd708SAndreas Gohr * Builds the feed's text. 171572dd708SAndreas Gohr * 172572dd708SAndreas Gohr * @return string the feed's complete text 173572dd708SAndreas Gohr */ 174572dd708SAndreas Gohr abstract public function createFeed(); 175572dd708SAndreas Gohr 176572dd708SAndreas Gohr /** 177d3233986SAndreas Gohr * Generate a filename for the feed cache file. The result will be $_SERVER["SCRIPT_NAME"] with the extension changed 178d3233986SAndreas Gohr * to .xml. For example: echo $_SERVER["SCRIPT_NAME"]."\n"; echo FeedCreator::_generateFilename(); would produce: 179572dd708SAndreas Gohr * /rss/latestnews.php 180572dd708SAndreas Gohr * latestnews.xml 181572dd708SAndreas Gohr * 182572dd708SAndreas Gohr * @return string the feed cache filename 183572dd708SAndreas Gohr * @since 1.4 184572dd708SAndreas Gohr * @access private 185572dd708SAndreas Gohr */ 186572dd708SAndreas Gohr protected function _generateFilename() 187572dd708SAndreas Gohr { 188d3233986SAndreas Gohr $fileInfo = pathinfo($_SERVER["SCRIPT_NAME"]); 189572dd708SAndreas Gohr 190572dd708SAndreas Gohr return substr($fileInfo["basename"], 0, -(strlen($fileInfo["extension"]) + 1)).".xml"; 191572dd708SAndreas Gohr } 192572dd708SAndreas Gohr 193572dd708SAndreas Gohr /** 194572dd708SAndreas Gohr * Send given file to Browser 195572dd708SAndreas Gohr * 196572dd708SAndreas Gohr * @since 1.4 197572dd708SAndreas Gohr * @param string $filename 198572dd708SAndreas Gohr */ 199572dd708SAndreas Gohr protected function _redirect($filename) 200572dd708SAndreas Gohr { 201572dd708SAndreas Gohr // attention, heavily-commented-out-area 202572dd708SAndreas Gohr 203572dd708SAndreas Gohr // maybe use this in addition to file time checking 204572dd708SAndreas Gohr //header("Expires: ".date("r",time()+$this->_timeout)); 205572dd708SAndreas Gohr 206572dd708SAndreas Gohr /* no caching at all, doesn't seem to work as good: 207572dd708SAndreas Gohr header("Cache-Control: no-cache"); 208572dd708SAndreas Gohr header("Pragma: no-cache"); 209572dd708SAndreas Gohr */ 210572dd708SAndreas Gohr 211572dd708SAndreas Gohr // HTTP redirect, some feed readers' simple HTTP implementations don't follow it 212572dd708SAndreas Gohr //header("Location: ".$filename); 213572dd708SAndreas Gohr 214572dd708SAndreas Gohr header("Content-Type: ".$this->contentType."; charset=".$this->encoding."; filename=".basename($filename)); 215572dd708SAndreas Gohr if (preg_match('/\.(kml|gpx)$/', $filename)) { 216572dd708SAndreas Gohr header("Content-Disposition: attachment; filename=".basename($filename)); 217572dd708SAndreas Gohr } else { 218572dd708SAndreas Gohr header("Content-Disposition: inline; filename=".basename($filename)); 219572dd708SAndreas Gohr } 220572dd708SAndreas Gohr readfile($filename); 221572dd708SAndreas Gohr exit(); 222572dd708SAndreas Gohr } 223572dd708SAndreas Gohr 224572dd708SAndreas Gohr /** 225572dd708SAndreas Gohr * Turns on caching and checks if there is a recent version of this feed in the cache. 226572dd708SAndreas Gohr * If there is, an HTTP redirect header is sent. 227572dd708SAndreas Gohr * To effectively use caching, you should create the FeedCreator object and call this method 228572dd708SAndreas Gohr * before anything else, especially before you do the time consuming task to build the feed 229572dd708SAndreas Gohr * (web fetching, for example). 230572dd708SAndreas Gohr * 231572dd708SAndreas Gohr * @since 1.4 232572dd708SAndreas Gohr * @param string $filename optional the filename where a recent version of the feed is saved. If not specified, 233d3233986SAndreas Gohr * the filename is $_SERVER["SCRIPT_NAME"] with the extension changed to .xml (see 234572dd708SAndreas Gohr * _generateFilename()). 235572dd708SAndreas Gohr * @param int $timeout optional the timeout in seconds before a cached version is refreshed (defaults to 236572dd708SAndreas Gohr * 3600 = 1 hour) 237572dd708SAndreas Gohr */ 238572dd708SAndreas Gohr public function useCached($filename = "", $timeout = 3600) 239572dd708SAndreas Gohr { 240572dd708SAndreas Gohr $this->_timeout = $timeout; 241572dd708SAndreas Gohr if ($filename == "") { 242572dd708SAndreas Gohr $filename = $this->_generateFilename(); 243572dd708SAndreas Gohr } 244572dd708SAndreas Gohr if (file_exists($filename) AND (time() - filemtime($filename) < $timeout)) { 245572dd708SAndreas Gohr $this->_redirect($filename); 246572dd708SAndreas Gohr } 247572dd708SAndreas Gohr } 248572dd708SAndreas Gohr 249572dd708SAndreas Gohr /** 250572dd708SAndreas Gohr * Saves this feed as a file on the local disk. After the file is saved, a redirect 251572dd708SAndreas Gohr * header may be sent to redirect the user to the newly created file. 252572dd708SAndreas Gohr * 253572dd708SAndreas Gohr * @since 1.4 254572dd708SAndreas Gohr * @param string $filename optional the filename where a recent version of the feed is saved. If not 255d3233986SAndreas Gohr * specified, the filename is $_SERVER["SCRIPT_NAME"] with the extension changed to .xml 256572dd708SAndreas Gohr * (see _generateFilename()). 257572dd708SAndreas Gohr * @param bool $displayContents optional send an HTTP redirect header or not. If true, the user will be 258572dd708SAndreas Gohr * automatically redirected to the created file. 259572dd708SAndreas Gohr */ 260572dd708SAndreas Gohr public function saveFeed($filename = "", $displayContents = true) 261572dd708SAndreas Gohr { 262572dd708SAndreas Gohr if ($filename == "") { 263572dd708SAndreas Gohr $filename = $this->_generateFilename(); 264572dd708SAndreas Gohr } 265572dd708SAndreas Gohr $feedFile = fopen($filename, "w+"); 266572dd708SAndreas Gohr if ($feedFile) { 267572dd708SAndreas Gohr fputs($feedFile, $this->createFeed()); 268572dd708SAndreas Gohr fclose($feedFile); 269572dd708SAndreas Gohr if ($displayContents) { 270572dd708SAndreas Gohr $this->_redirect($filename); 271572dd708SAndreas Gohr } 272572dd708SAndreas Gohr } else { 273572dd708SAndreas Gohr echo "<br /><b>Error creating feed file, please check write permissions.</b><br />"; 274572dd708SAndreas Gohr } 275572dd708SAndreas Gohr } 276572dd708SAndreas Gohr} 277