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