xref: /dokuwiki/vendor/openpsa/universalfeedcreator/lib/Creator/FeedCreator.php (revision 2cadabe7de17786c9119ff71ba98edc20715e781)
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