PluginProbe
FeedWordPress / 0.98
FeedWordPress v0.98
trunk 0.8 0.9 0.91 0.95 0.96 0.97 0.98 0.981 0.99 0.991 0.992 0.993 2008.1030 2008.1101 2008.1105 2008.1214 2009.0612 2009.0613 2009.0618 2009.0707 2009.1111 2009.1112 2010.0127 2010.0528 All 65 releases
← All changes | OPTIONAL/wp-includes/rss-functions.php +663 -276 0.960.98 View file →
@@ -3,9 +3,9 @@
3 3 * File: A compiled file for RSS syndication
4 4 * Author: Kellan Elliot-McCrea <kellan@protest.net>
5 5 * WordPress development team <http://www.wordpress.org/>
6 6 * Charles Johnson <technophilia@radgeek.com>
7 - * Version: 0.7wp (2005.05.07)
7 + * Version: 0.8wp (2005.10.14)
8 8 * License: GPL
9 9 *
10 10 * Provenance:
11 11 *
@@ -10,15 +10,16 @@
10 10 * Provenance:
11 11 *
12 12 * This is a drop-in replacement for the `rss-functions.php` provided with the
13 13 * WordPress 1.5 distribution, which upgrades the version of MagpieRSS from 0.51
14 - * to a modification of 0.7. In addition to improved handling of character
15 - * encoding and other updates, this branch of MagpieRSS 0.7 also supports
16 - * multiple categorization of posts (using <dc:subject> or <category>). The
17 - * file is, therefore, derived from four sources: (1) Kellan's MagpieRSS 0.51,
18 - * (2) the WordPress development team's modifications to MagpieRSS 0.51,
19 - * (3) Kellan's MagpieRSS 0.7, and (4) Charles Johnson's modifications to
20 - * MagpieRSS 0.7. All possible because of the GPL. Yay for free software!
14 + * to 0.8a. The update improves handling of character encoding, supports
15 + * multiple categories for posts (using <dc:subject> or <category>), supports
16 + * Atom 1.0, and implements many other useful features. The file is derived from
17 + * a combination of (1) the WordPress development team's modifications to
18 + * MagpieRSS 0.51 and (2) the latest bleeding-edge updates to the "official"
19 + * MagpieRSS software, including Kellan's original work and some substantial
20 + * updates by Charles Johnson. All possible through the magic of the GPL. Yay
21 + * for free software!
21 22 *
22 23 * Differences from the main branch of MagpieRSS:
23 24 *
24 25 * 1. Everything in rss_parse.inc, rss_fetch.inc, rss_cache.inc, and
@@ -32,39 +33,76 @@
32 33 * than writing external files directly.
33 34 *
34 35 * 4. There are two WordPress-specific functions, get_rss() and wp_rss()
35 36 *
36 - * 5. New cases added to MagpieRSS::feed_start_element(),
37 - * MagpieRSS::feed_end_element(), and MagpieRSS::normalize() to handle:
37 + * Differences from the version of MagpieRSS packaged with WordPress:
38 38 *
39 - * (a) Multiple categories
40 - * (b) RSS 2.0 and Atom 0.6+ enclosures
39 + * 1. Support for translation between multiple character encodings. Under
40 + * PHP 5 this is very nicely handled by the XML parsing library. Under PHP
41 + * 4 we need to do a little bit of work ourselves, using either iconv or
42 + * mb_convert_encoding if it is not one of the (extremely limited) number
43 + * of character sets that PHP 4's XML module can handle natively.
41 44 *
42 - * Categories are stored in $item['category'], $item['categories'],
43 - * $item['dc']['subject'], and $item['dc']['subjects'] (the singular keys
44 - * point to string values for the first category used; the plural keys
45 - * point to an array of all the categories the item is in)
45 + * 2. Numerous bug fixes.
46 46 *
47 - * Enclosures are stored in the array $item['enclosure'] as suggested
48 - * at <http://magpie.laughingmeme.org/blog/?p=101>. So the URL of the first
49 - * enclosure is $item['enclosure'][0]['url']; the length is
50 - * $item['enclosure'][0]['length']; and the type is
51 - * $item['enclosure'][0]['type']
47 + * 3. The parser class MagpieRSS has been substantially revised to better
48 + * support popular features such as enclosures and multiple categories,
49 + * and to support the new Atom 1.0 IETF standard. (Atom feeds are
50 + * normalized so as to make the data available using terminology from
51 + * either Atom 0.3 or Atom 1.0. Atom 0.3 backward-compatibility is provided
52 + * to allow existing software to easily begin accepting Atom 1.0 data; new
53 + * software SHOULD NOT depend on the 0.3 terminology, but rather use the
54 + * normalization as a convenient way to keep supporting 0.3 feeds while
55 + * they linger in the world.)
52 56 *
53 - * Note that these are hacked-in solutions for inherited problems with
54 - * MagpieRSS as of version 0.7. They are not guaranteed to be
55 - * forward-compatible when/if Kellan solves these problems in the official
56 - * Magpie branch in the future. If you have filters (for example) that
57 - * depend on either categories or enclosures working as they currently do,
58 - * keep an eye on the ChangeLog in future releases.
57 + * The upgraded MagpieRSS can also now handle some content constructs that
58 + * had not been handled well by previous versions of Magpie (such as the
59 + * use of namespaced XHTML in <xhtml:body> or <xhtml:div> elements to
60 + * provide the full content of posts in RSS 2.0 feeds).
61 + *
62 + * Unlike previous versions of MagpieRSS, this version can parse multiple
63 + * instances of the same child element in item/entry and channel/feed
64 + * containers. This is done using simple counters next to the element
65 + * names: the first <category> element on an RSS item, for example, can be
66 + * found in $item['category'] (thus preserving backward compatibility); the
67 + * second in $item['category#2'], the third in $item['category#3'], and so
68 + * on. The number of categories applied to the item can be found in
69 + * $item['category#']
70 + *
71 + * Also unlike previous versions of MagpieRSS, this version allows you to
72 + * access the values of elements' attributes as well as the content they
73 + * contain. This can be done using a simple syntax inspired by XPath: to
74 + * access the type attribute of an RSS 2.0 enclosure, for example, you
75 + * need only access `$item['enclosure@type']`. A comma-separated list of
76 + * attributes for the enclosure element is stored in `$item['enclosure@']`.
77 + * (This syntax interacts easily with the syntax for multiple categories;
78 + * for example, the value of the `scheme` attribute for the fourth category
79 + * element on a particular item is stored in `$item['category#4@scheme']`.)
80 + *
81 + * Note also that this implementation IS NOT backward-compatible with the
82 + * kludges that were used to hack in support for multiple categories and
83 + * for enclosures in upgraded versions of MagpieRSS distributed with
84 + * previous versions of FeedWordPress. If your hacks or filter plugins
85 + * depended on the old way of doing things... well, I warned you that they
86 + * might not be permanent. Sorry!
59 87 */
60 88
61 89 define('RSS', 'RSS');
62 90 define('ATOM', 'Atom');
63 -define('MAGPIE_USER_AGENT', 'WordPress/' . $wp_version);
64 91
65 -# UPDATED: rss_parse.inc: class MagpieRSS, function map_attrs
66 -# --- cut here ---
92 +################################################################################
93 +## WordPress: make some settings WordPress-appropriate #########################
94 +################################################################################
95 +
96 +define('MAGPIE_USER_AGENT', 'WordPress/' . $wp_version . '(+http://www.wordpress.org)');
97 +
98 +$wp_encoding = get_settings('blog_charset');
99 +define('MAGPIE_OUTPUT_ENCODING', ($wp_encoding?$wp_encoding:'ISO-8859-1'));
100 +
101 +################################################################################
102 +## rss_parse.inc: from MagpieRSS 0.8a ##########################################
103 +################################################################################
104 +
67 105 /**
68 106 * Hybrid parser, and object, takes RSS as a string and returns a simple object.
69 107 *
70 108 * see: rss_fetch.inc for a simpler interface with integrated caching support
@@ -88,9 +126,14 @@
88 126 var $WARNING = "";
89 127
90 128 // define some constants
91 129
92 - var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
130 + var $_ATOM_CONTENT_CONSTRUCTS = array(
131 + 'content', 'summary', 'title', /* common */
132 + 'info', 'tagline', 'copyright', /* Atom 0.3 */
133 + 'rights', 'subtitle', /* Atom 1.0 */
134 + );
135 + var $_XHTML_CONTENT_CONSTRUCTS = array('body', 'div');
93 136 var $_KNOWN_ENCODINGS = array('UTF-8', 'US-ASCII', 'ISO-8859-1');
94 137
95 138 // parser variables, useless if you're not a parser, treat as private
96 139 var $stack = array(); // parser stack
@@ -95,16 +138,16 @@
95 138 // parser variables, useless if you're not a parser, treat as private
96 139 var $stack = array(); // parser stack
97 140 var $inchannel = false;
98 141 var $initem = false;
99 - var $incontent = false; // if in Atom <content mode="xml"> field
142 +
143 + var $incontent = array(); // non-empty if in namespaced XML content field
144 + var $exclude_top = false; // true when Atom 1.0 type="xhtml"
145 +
100 146 var $intextinput = false;
101 147 var $inimage = false;
102 148 var $current_namespace = false;
103 149
104 - var $incategory = false;
105 - var $current_category = 0;
106 -
107 150 /**
108 151 * Set up XML parser, parse source, and return populated RSS object..
109 152 *
110 153 * @param string $source string containing the RSS to be parsed
@@ -163,9 +206,9 @@
163 206 xml_set_element_handler($this->parser,
164 207 'feed_start_element', 'feed_end_element' );
165 208
166 209 xml_set_character_data_handler( $this->parser, 'feed_cdata' );
167 -
210 +
168 211 $status = xml_parse( $this->parser, $source );
169 212
170 213 if (! $status ) {
171 214 $errorcode = xml_get_error_code( $this->parser );
@@ -188,16 +231,18 @@
188 231 $el = $element = strtolower($element);
189 232 $attrs = array_change_key_case($attrs, CASE_LOWER);
190 233
191 234 // check for a namespace, and split if found
192 - $ns = false;
193 - if ( strpos( $element, ':' ) ) {
194 - list($ns, $el) = split( ':', $element, 2);
195 - }
196 - if ( $ns and $ns != 'rdf' ) {
197 - $this->current_namespace = $ns;
198 - }
199 -
235 + if ( empty($this->incontent) ) { // Don't munge content tags
236 + $ns = false;
237 + if ( strpos( $element, ':' ) ) {
238 + list($ns, $el) = split( ':', $element, 2);
239 + }
240 + if ( $ns and $ns != 'rdf' ) {
241 + $this->current_namespace = $ns;
242 + }
243 + }
244 +
200 245 # if feed type isn't set, then this is first element of feed
201 246 # identify feed from root element
202 247 #
203 248 if (!isset($this->feed_type) ) {
@@ -210,18 +255,40 @@
210 255 $this->feed_version = $attrs['version'];
211 256 }
212 257 elseif ( $el == 'feed' ) {
213 258 $this->feed_type = ATOM;
214 - $this->feed_version = $attrs['version'];
259 + if ($attrs['xmlns'] == 'http://www.w3.org/2005/Atom') { // Atom 1.0
260 + $this->feed_version = '1.0';
261 + }
262 + else { // Atom 0.3, probably.
263 + $this->feed_version = $attrs['version'];
264 + }
215 265 $this->inchannel = true;
216 266 }
217 267 return;
218 268 }
219 269
220 - if ( $el == 'channel' )
270 + // if we're inside a namespaced content construct, treat tags as text
271 + if ( !empty($this->incontent) )
221 272 {
273 + if ((count($this->incontent) > 1) or !$this->exclude_top) {
274 + // if tags are inlined, then flatten
275 + $attrs_str = join(' ',
276 + array_map('map_attrs',
277 + array_keys($attrs),
278 + array_values($attrs) ) );
279 + if (strlen($attrs_str) > 0) $attrs_str = ' '.$attrs_str;
280 +
281 + $this->append_content( "<{$element}{$attrs_str}>" );
282 + }
283 + array_push($this->incontent, $el); // stack for parsing content XML
284 + }
285 +
286 + elseif ( $el == 'channel' )
287 + {
222 288 $this->inchannel = true;
223 289 }
290 +
224 291 elseif ($el == 'item' or $el == 'entry' )
225 292 {
226 293 $this->initem = true;
227 294 if ( isset($attrs['rdf:about']) ) {
@@ -227,14 +294,9 @@
227 294 if ( isset($attrs['rdf:about']) ) {
228 295 $this->current_item['about'] = $attrs['rdf:about'];
229 296 }
230 297 }
231 -
232 - elseif ($this->initem and ($el == 'category' or ($this->current_namespace == 'dc' and $el == 'subject'))) {
233 - $this->incategory = true;
234 - array_unshift( $this->stack, $el );
235 - }
236 -
298 +
237 299 // if we're in the default namespace of an RSS feed,
238 300 // record textinput or image fields
239 301 elseif (
240 302 $this->feed_type == RSS and
@@ -251,76 +313,81 @@
251 313 {
252 314 $this->inimage = true;
253 315 }
254 316
255 - # -- Handle RSS 2 enclosures. Suggested by <http://magpie.laughingmeme.org/blog/?p=101>
256 - elseif (
257 - $this->feed_type == RSS and
258 - $el == 'enclosure' )
259 - {
260 - $this->current_item[$el][] = $attrs;
261 - $this->incontent = $el;
262 - }
317 + // set stack[0] to current element
318 + else {
319 + // Atom support many links per containing element.
320 + // Magpie treats link elements of type rel='alternate'
321 + // as being equivalent to RSS's simple link element.
263 322
264 - # handle atom content constructs
265 - elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
266 - {
267 - // avoid clashing w/ RSS mod_content
268 - if ($el == 'content' ) {
269 - $el = 'atom_content';
270 - }
271 -
272 - $this->incontent = $el;
273 -
274 -
275 - }
323 + $atom_link = false;
324 + if ($this->feed_type == ATOM and $el == 'link') {
325 + $atom_link = true;
326 + if (isset($attrs['rel']) and $attrs['rel'] != 'alternate') {
327 + $el = $el . "_" . $attrs['rel']; // pseudo-element names for Atom link elements
328 + }
329 + }
330 + # handle atom content constructs
331 + elseif ( $this->feed_type == ATOM and in_array($el, $this->_ATOM_CONTENT_CONSTRUCTS) )
332 + {
333 + // avoid clashing w/ RSS mod_content
334 + if ($el == 'content' ) {
335 + $el = 'atom_content';
336 + }
276 337
277 - // if inside an Atom content construct (e.g. content or summary) field treat tags as text
278 - elseif ($this->feed_type == ATOM and $this->incontent )
279 - {
280 - // if tags are inlined, then flatten
281 - $attrs_str = join(' ',
282 - array_map('map_attrs',
283 - array_keys($attrs),
284 - array_values($attrs) ) );
285 -
286 - $this->append_content( "<$element $attrs_str>" );
287 -
288 - array_unshift( $this->stack, $el );
289 - }
290 -
291 - // Atom support many links per containging element.
292 - // Magpie treats link elements of type rel='alternate'
293 - // as being equivalent to RSS's simple link element.
294 - //
295 - elseif ($this->feed_type == ATOM and $el == 'link' )
296 - {
297 - # -- CWJ: Treat <link> elements without explicit rel as rel="alternate"
298 - if ( !isset($attrs['rel']) or isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
299 - {
300 - $link_el = 'link';
301 - }
302 - # -- CWJ: support Atom 0.6+ enclosures
303 - elseif ( isset($attrs['rel']) and $attrs['rel'] == 'enclosure' )
304 - {
305 - $link_el = 'link_' . $attrs['rel'];
306 -
307 - # -- CWJ: Normalize to RSS 2.0 enclosure handling
308 - $n = count($this->current_item[$attrs['rel']]);
309 - $this->current_item[$attrs['rel']][$n] = $attrs;
310 - $this->current_item[$attrs['rel']][$n]['url'] =
311 - $this->current_item[$attrs['rel']][$n]['href'];
312 - }
313 - else {
314 - $link_el = 'link_' . $attrs['rel'];
315 - }
316 -
317 - $this->append($link_el, $attrs['href']);
318 - }
338 + // assume that everything accepts namespaced XML
339 + // (that will pass through some non-validating feeds;
340 + // but so what? this isn't a validating parser)
341 + $this->incontent = array();
342 + array_push($this->incontent, $el); // start a stack
319 343
320 - // set stack[0] to current element
321 - else {
322 - array_unshift($this->stack, $el);
344 + if (
345 + isset($attrs['type'])
346 + and trim(strtolower($attrs['type']))=='xhtml'
347 + ) {
348 + $this->exclude_top = true;
349 + } else {
350 + $this->exclude_top = false;
351 + }
352 + }
353 + # Handle inline XHTML body elements --CWJ
354 + elseif (
355 + ($this->current_namespace=='xhtml' or (isset($attrs['xmlns']) and $attrs['xmlns'] == 'http://www.w3.org/1999/xhtml'))
356 + and in_array($el, $this->_XHTML_CONTENT_CONSTRUCTS) )
357 + {
358 + $this->current_namespace = 'xhtml';
359 + $this->incontent = array();
360 + array_push($this->incontent, $el); // start a stack
361 + $this->exclude_top = false;
362 + }
363 +
364 + array_unshift($this->stack, $el);
365 + $elpath = join('_', array_reverse($this->stack));
366 +
367 + $n = $this->element_count($elpath);
368 + $this->element_count($elpath, $n+1);
369 +
370 + if ($n > 0) {
371 + array_shift($this->stack);
372 + array_unshift($this->stack, $el.'#'.($n+1));
373 + $elpath = join('_', array_reverse($this->stack));
374 + }
375 +
376 + // this makes the baby Jesus cry, but we can't do it in normalize()
377 + // because we've made the element name for Atom links unpredictable
378 + // by tacking on the relation to the end. -CWJ
379 + if ($atom_link and isset($attrs['href'])) {
380 + $this->append($elpath, $attrs['href']);
381 + }
382 +
383 + // add attributes
384 + if (count($attrs) > 0) {
385 + $this->append($elpath.'@', join(',', array_keys($attrs)));
386 + foreach ($attrs as $attr => $value) {
387 + $this->append($elpath.'@'.$attr, $value);
388 + }
389 + }
323 390 }
324 391 }
325 392
326 393
@@ -325,10 +392,10 @@
325 392
326 393
327 394
328 395 function feed_cdata ($p, $text) {
329 - if ($this->feed_type == ATOM and $this->incontent)
330 - {
396 +
397 + if ($this->incontent) {
331 398 $this->append_content( $text );
332 399 }
333 400 else {
334 401 $current_el = join('_', array_reverse($this->stack));
@@ -338,9 +405,34 @@
338 405
339 406 function feed_end_element ($p, $el) {
340 407 $el = strtolower($el);
341 408
342 - if ( $el == 'item' or $el == 'entry' )
409 + if ( $this->incontent ) {
410 + $opener = array_pop($this->incontent);
411 +
412 + // Don't get bamboozled by namespace voodoo
413 + if (strpos($el, ':')) { list($ns, $closer) = split(':', $el); }
414 + else { $ns = false; $closer = $el; }
415 +
416 + // Don't get bamboozled by our munging of <atom:content>, either
417 + if ($this->feed_type == ATOM and $closer == 'content') {
418 + $closer = 'atom_content';
419 + }
420 +
421 + // balance tags properly
422 + // note: i don't think this is actually neccessary
423 + if ($opener != $closer) {
424 + array_push($this->incontent, $opener);
425 + $this->append_content("<$el />");
426 + } elseif ($this->incontent) { // are we in the content construct still?
427 + if ((count($this->incontent) > 1) or !$this->exclude_top) {
428 + $this->append_content("</$el>");
429 + }
430 + } else { // shift the opening of the content construct off the normal stack
431 + array_shift( $this->stack );
432 + }
433 + }
434 + elseif ( $el == 'item' or $el == 'entry' )
343 435 {
344 436 $this->items[] = $this->current_item;
345 437 $this->current_item = array();
346 438 $this->initem = false;
@@ -346,14 +438,9 @@
346 438 $this->initem = false;
347 439
348 440 $this->current_category = 0;
349 441 }
350 - elseif ($this->initem and ($el == 'category' or $el == 'dc:subject')) {
351 - $this->incategory = false;
352 - $this->current_category = $this->current_category + 1;
353 - array_shift( $this->stack );
354 - }
355 - elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
442 + elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
356 443 {
357 444 $this->intextinput = false;
358 445 }
359 446 elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
@@ -359,34 +446,19 @@
359 446 elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
360 447 {
361 448 $this->inimage = false;
362 449 }
363 - elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
364 - {
365 - $this->incontent = false;
366 - }
367 450 elseif ($el == 'channel' or $el == 'feed' )
368 451 {
369 452 $this->inchannel = false;
370 453 }
371 - elseif ($this->feed_type == ATOM and $this->incontent ) {
372 - // balance tags properly
373 - // note: i don't think this is actually neccessary
374 - if ( $this->stack[0] == $el )
375 - {
376 - $this->append_content("</$el>");
377 - }
378 - else {
379 - $this->append_content("<$el />");
380 - }
381 -
382 - array_shift( $this->stack );
383 - }
384 454 else {
385 - array_shift( $this->stack );
455 + array_shift( $this->stack );
386 456 }
387 457
388 - $this->current_namespace = false;
458 + if ( !$this->incontent ) { // Don't munge the namespace after finishing with elements in namespaced content constructs -CWJ
459 + $this->current_namespace = false;
460 + }
389 461 }
390 462
391 463 function concat (&$str1, $str2="") {
392 464 if (!isset($str1) ) {
@@ -394,17 +466,23 @@
394 466 }
395 467 $str1 .= $str2;
396 468 }
397 469
398 -
399 -
400 470 function append_content($text) {
401 - if ( $this->initem ) {
402 - $this->concat( $this->current_item[ $this->incontent ], $text );
403 - }
404 - elseif ( $this->inchannel ) {
405 - $this->concat( $this->channel[ $this->incontent ], $text );
406 - }
471 + if ( $this->initem ) {
472 + if ($this->current_namespace) {
473 + $this->concat( $this->current_item[$this->current_namespace][ reset($this->incontent) ], $text );
474 + } else {
475 + $this->concat( $this->current_item[ reset($this->incontent) ], $text );
476 + }
477 + }
478 + elseif ( $this->inchannel ) {
479 + if ($this->current_namespace) {
480 + $this->concat( $this->channel[$this->current_namespace][ reset($this->incontent) ], $text );
481 + } else {
482 + $this->concat( $this->channel[ reset($this->incontent) ], $text );
483 + }
484 + }
407 485 }
408 486
409 487 // smart append - field and namespace aware
410 488 function append($el, $text) {
@@ -412,19 +490,16 @@
412 490 return;
413 491 }
414 492 if ( $this->current_namespace )
415 493 {
416 - if ( $this->incategory ) {
417 - $this->concat( $this->current_item['categories'][$this->current_category], $text );
494 + if ( $this->initem ) {
495 + $this->concat(
496 + $this->current_item[ $this->current_namespace ][ $el ], $text);
418 497 }
419 - elseif ( $this->initem ) {
420 - $this->concat(
421 - $this->current_item[ $this->current_namespace ][ $el ], $text );
422 - }
423 498 elseif ($this->inchannel) {
424 - $this->concat(
425 - $this->channel[ $this->current_namespace][ $el ], $text );
426 - }
499 + $this->concat(
500 + $this->channel[ $this->current_namespace][ $el ], $text );
501 + }
427 502 elseif ($this->intextinput) {
428 503 $this->concat(
429 504 $this->textinput[ $this->current_namespace][ $el ], $text );
430 505 }
@@ -433,15 +508,12 @@
433 508 $this->image[ $this->current_namespace ][ $el ], $text );
434 509 }
435 510 }
436 511 else {
437 - if ( $this->incategory ) {
438 - $this->concat( $this->current_item['categories'][$this->current_category], $text );
512 + if ( $this->initem ) {
513 + $this->concat(
514 + $this->current_item[ $el ], $text);
439 515 }
440 - elseif ( $this->initem ) {
441 - $this->concat(
442 - $this->current_item[ $el ], $text);
443 - }
444 516 elseif ($this->intextinput) {
445 517 $this->concat(
446 518 $this->textinput[ $el ], $text );
447 519 }
@@ -449,52 +521,269 @@
449 521 $this->concat(
450 522 $this->image[ $el ], $text );
451 523 }
452 524 elseif ($this->inchannel) {
453 - $this->concat(
454 - $this->channel[ $el ], $text );
525 + $this->concat(
526 + $this->channel[ $el ], $text );
455 527 }
456 528
457 529 }
458 530 }
531 +
532 + // smart count - field and namespace aware
533 + function element_count ($el, $set = NULL) {
534 + if (!$el) {
535 + return;
536 + }
537 + if ( $this->current_namespace )
538 + {
539 + if ( $this->initem ) {
540 + if (!is_null($set)) { $this->current_item[ $this->current_namespace ][ $el.'#' ] = $set; }
541 + $ret = (isset($this->current_item[ $this->current_namespace ][ $el.'#' ]) ?
542 + $this->current_item[ $this->current_namespace ][ $el.'#' ] : 0);
543 + }
544 + elseif ($this->inchannel) {
545 + if (!is_null($set)) { $this->channel[ $this->current_namespace ][ $el.'#' ] = $set; }
546 + $ret = (isset($this->channel[ $this->current_namespace][ $el.'#' ]) ?
547 + $this->channel[ $this->current_namespace][ $el.'#' ] : 0);
548 + }
549 + }
550 + else {
551 + if ( $this->initem ) {
552 + if (!is_null($set)) { $this->current_item[ $el.'#' ] = $set; }
553 + $ret = (isset($this->current_item[ $el.'#' ]) ?
554 + $this->current_item[ $el.'#' ] : 0);
555 + }
556 + elseif ($this->inchannel) {
557 + if (!is_null($set)) {$this->channel[ $el.'#' ] = $set; }
558 + $ret = (isset($this->channel[ $el.'#' ]) ?
559 + $this->channel[ $el.'#' ] : 0);
560 + }
561 + }
562 + return $ret;
563 + }
564 +
565 + function normalize_enclosure (&$source, $from, &$dest, $to, $i) {
566 + $id_from = $this->element_id($from, $i);
567 + $id_to = $this->element_id($to, $i);
568 + if (isset($source["{$id_from}@"])) {
569 + foreach (explode(',', $source["{$id_from}@"]) as $attr) {
570 + if ($from=='link_enclosure' and $attr=='href') { // from Atom
571 + $dest["{$id_to}@url"] = $source["{$id_from}@{$attr}"];
572 + $dest["{$id_to}"] = $source["{$id_from}@{$attr}"];
573 + }
574 + elseif ($from=='enclosure' and $attr=='url') { // from RSS
575 + $dest["{$id_to}@href"] = $source["{$id_from}@{$attr}"];
576 + $dest["{$id_to}"] = $source["{$id_from}@{$attr}"];
577 + }
578 + else {
579 + $dest["{$id_to}@{$attr}"] = $source["{$id_from}@{$attr}"];
580 + }
581 + }
582 + }
583 + }
584 +
585 + function normalize_atom_person (&$source, $person, &$dest, $to, $i) {
586 + $id = $this->element_id($person, $i);
587 + $id_to = $this->element_id($to, $i);
588 +
589 + // Atom 0.3 <=> Atom 1.0
590 + if ($this->feed_version >= 1.0) { $used = 'uri'; $norm = 'url'; }
591 + else { $used = 'url'; $norm = 'uri'; }
592 +
593 + if (isset($source["{$id}_{$used}"])) {
594 + $dest["{$id_to}_{$norm}"] = $source["{$id}_{$used}"];
595 + }
596 +
597 + // Atom to RSS 2.0 and Dublin Core
598 + // RSS 2.0 person strings should be valid e-mail addresses if possible.
599 + if (isset($source["{$id}_email"])) {
600 + $rss_author = $source["{$id}_email"];
601 + }
602 + if (isset($source["{$id}_name"])) {
603 + $rss_author = $source["{$id}_name"]
604 + . (isset($rss_author) ? " <$rss_author>" : '');
605 + }
606 + if (isset($rss_author)) {
607 + $source[$id] = $rss_author; // goes to top-level author or contributor
608 + $dest[$id_to] = $rss_author; // goes to dc:creator or dc:contributor
609 + }
610 + }
611 +
612 + // Normalize Atom 1.0 and RSS 2.0 categories to Dublin Core...
613 + function normalize_category (&$source, $from, &$dest, $to, $i) {
614 + $cat_id = $this->element_id($from, $i);
615 + $dc_id = $this->element_id($to, $i);
616 +
617 + // first normalize category elements: Atom 1.0 <=> RSS 2.0
618 + if ( isset($source["{$cat_id}@term"]) ) { // category identifier
619 + $source[$cat_id] = $source["{$cat_id}@term"];
620 + } elseif ( $this->feed_type == RSS ) {
621 + $source["{$cat_id}@term"] = $source[$cat_id];
622 + }
623 +
624 + if ( isset($source["{$cat_id}@scheme"]) ) { // URI to taxonomy
625 + $source["{$cat_id}@domain"] = $source["{$cat_id}@scheme"];
626 + } elseif ( isset($source["{$cat_id}@domain"]) ) {
627 + $source["{$cat_id}@scheme"] = $source["{$cat_id}@domain"];
628 + }
629 +
630 + // Now put the identifier into dc:subject
631 + $dest[$dc_id] = $source[$cat_id];
632 + }
459 633
634 + // ... or vice versa
635 + function normalize_dc_subject (&$source, $from, &$dest, $to, $i) {
636 + $dc_id = $this->element_id($from, $i);
637 + $cat_id = $this->element_id($to, $i);
638 +
639 + $dest[$cat_id] = $source[$dc_id]; // RSS 2.0
640 + $dest["{$cat_id}@term"] = $source[$dc_id]; // Atom 1.0
641 + }
642 +
643 + // simplify the logic for normalize(). Makes sure that count of elements and
644 + // each of multiple elements is normalized properly. If you need to mess
645 + // with things like attributes or change formats or the like, pass it a
646 + // callback to handle each element.
647 + function normalize_element (&$source, $from, &$dest, $to, $via = NULL) {
648 + if (isset($source[$from]) or isset($source["{$from}#"])) {
649 + if (isset($source["{$from}#"])) {
650 + $n = $source["{$from}#"];
651 + $dest["{$to}#"] = $source["{$from}#"];
652 + }
653 + else { $n = 1; }
654 +
655 + for ($i = 1; $i <= $n; $i++) {
656 + if (isset($via)) { // custom callback for ninja attacks
657 + $this->{$via}($source, $from, $dest, $to, $i);
658 + }
659 + else { // just make it the same
660 + $from_id = $this->element_id($from, $i);
661 + $to_id = $this->element_id($to, $i);
662 + $dest[$to_id] = $source[$from_id];
663 + }
664 + }
665 + }
666 + }
667 +
460 668 function normalize () {
461 - // if atom populate rss fields
669 + // if atom populate rss fields and normalize 0.3 and 1.0 feeds
462 670 if ( $this->is_atom() ) {
463 - $this->channel['description'] = $this->channel['tagline'];
464 - for ( $i = 0; $i < count($this->items); $i++) {
465 - $item = $this->items[$i];
466 - if ( isset($item['summary']) )
467 - $item['description'] = $item['summary'];
468 - if ( isset($item['atom_content']))
469 - $item['content']['encoded'] = $item['atom_content'];
470 -
471 - $atom_date = (isset($item['issued']) ) ? $item['issued'] : $item['modified'];
472 - if ( $atom_date ) {
473 - $epoch = @parse_w3cdtf($item['modified']);
474 - if ($epoch and $epoch > 0) {
475 - $item['date_timestamp'] = $epoch;
476 - }
477 - }
671 + // Atom 1.0 elements <=> Atom 0.3 elements (Thanks, o brilliant wordsmiths of the Atom 1.0 standard!)
672 + if ($this->feed_version < 1.0) {
673 + $this->normalize_element($this->channel, 'tagline', $this->channel, 'subtitle');
674 + $this->normalize_element($this->channel, 'copyright', $this->channel, 'rights');
675 + $this->normalize_element($this->channel, 'modified', $this->channel, 'updated');
676 + } else {
677 + $this->normalize_element($this->channel, 'subtitle', $this->channel, 'tagline');
678 + $this->normalize_element($this->channel, 'rights', $this->channel, 'copyright');
679 + $this->normalize_element($this->channel, 'updated', $this->channel, 'modified');
680 + }
681 + $this->normalize_element($this->channel, 'author', $this->channel['dc'], 'creator', 'normalize_atom_person');
682 + $this->normalize_element($this->channel, 'contributor', $this->channel['dc'], 'contributor', 'normalize_atom_person');
478 683
479 - if ( is_array($item['categories']) ) {
480 - $item['category'] = $item['categories'][0];
481 - $item['dc']['subjects'] = $item['categories'];
482 - $item['dc']['subject'] = $item['category'];
483 - }
484 -
485 - $this->items[$i] = $item;
486 - }
684 + // Atom elements to RSS elements
685 + $this->normalize_element($this->channel, 'subtitle', $this->channel, 'description');
686 +
687 + if ( isset($this->channel['logo']) ) {
688 + $this->normalize_element($this->channel, 'logo', $this->image, 'url');
689 + $this->normalize_element($this->channel, 'link', $this->image, 'link');
690 + $this->normalize_element($this->channel, 'title', $this->image, 'title');
691 + }
692 +
693 + for ( $i = 0; $i < count($this->items); $i++) {
694 + $item = $this->items[$i];
695 +
696 + // Atom 1.0 elements <=> Atom 0.3 elements
697 + if ($this->feed_version < 1.0) {
698 + $this->normalize_element($item, 'modified', $item, 'updated');
699 + $this->normalize_element($item, 'issued', $item, 'published');
700 + } else {
701 + $this->normalize_element($item, 'updated', $item, 'modified');
702 + $this->normalize_element($item, 'published', $item, 'issued');
703 + }
704 +
705 + // "If an atom:entry element does not contain
706 + // atom:author elements, then the atom:author elements
707 + // of the contained atom:source element are considered
708 + // to apply. In an Atom Feed Document, the atom:author
709 + // elements of the containing atom:feed element are
710 + // considered to apply to the entry if there are no
711 + // atom:author elements in the locations described
712 + // above." <http://atompub.org/2005/08/17/draft-ietf-atompub-format-11.html#rfc.section.4.2.1>
713 + if (!isset($item["author#"])) {
714 + if (isset($item["source_author#"])) { // from aggregation source
715 + $source = $item;
716 + $author = "source_author";
717 + } elseif (isset($this->channel["author#"])) { // from containing feed
718 + $source = $this->channel;
719 + $author = "author";
720 + }
721 +
722 + $item["author#"] = $source["{$author}#"];
723 + for ($au = 1; $au <= $item["author#"]; $au++) {
724 + $id_to = $this->element_id('author', $au);
725 + $id_from = $this->element_id($author, $au);
726 +
727 + $item[$id_to] = $source[$id_from];
728 + foreach (array('name', 'email', 'uri', 'url') as $what) {
729 + if (isset($source["{$id_from}_{$what}"])) {
730 + $item["{$id_to}_{$what}"] = $source["{$id_from}_{$what}"];
731 + }
732 + }
733 + }
734 + }
735 +
736 + // Atom elements to RSS elements
737 + $this->normalize_element($item, 'author', $item['dc'], 'creator', 'normalize_atom_person');
738 + $this->normalize_element($item, 'contributor', $item['dc'], 'contributor', 'normalize_atom_person');
739 + $this->normalize_element($item, 'summary', $item, 'description');
740 + $this->normalize_element($item, 'atom_content', $item['content'], 'encoded');
741 + $this->normalize_element($item, 'link_enclosure', $item, 'enclosure', 'normalize_enclosure');
742 +
743 + // Categories
744 + if ( isset($item['category#']) ) { // Atom 1.0 categories to dc:subject and RSS 2.0 categories
745 + $this->normalize_element($item, 'category', $item['dc'], 'subject', 'normalize_category');
746 + }
747 + elseif ( isset($item['dc']['subject#']) ) { // dc:subject to Atom 1.0 and RSS 2.0 categories
748 + $this->normalize_element($item['dc'], 'subject', $item, 'category', 'normalize_dc_subject');
749 + }
750 +
751 + // Normalized item timestamp
752 + $atom_date = (isset($item['published']) ) ? $item['published'] : $item['updated'];
753 + if ( $atom_date ) {
754 + $epoch = @parse_w3cdtf($atom_date);
755 + if ($epoch and $epoch > 0) {
756 + $item['date_timestamp'] = $epoch;
757 + }
758 + }
759 +
760 + $this->items[$i] = $item;
761 + }
487 762 }
488 763 elseif ( $this->is_rss() ) {
489 - $this->channel['tagline'] = $this->channel['description'];
764 + // RSS elements to Atom elements
765 + $this->normalize_element($this->channel, 'description', $this->channel, 'tagline'); // Atom 0.3
766 + $this->normalize_element($this->channel, 'description', $this->channel, 'subtitle'); // Atom 1.0 (yay wordsmithing!)
767 + $this->normalize_element($this->image, 'url', $this->channel, 'logo');
768 +
490 769 for ( $i = 0; $i < count($this->items); $i++) {
491 770 $item = $this->items[$i];
492 - if ( isset($item['description']))
493 - $item['summary'] = $item['description'];
494 - if ( isset($item['content']['encoded'] ) )
495 - $item['atom_content'] = $item['content']['encoded'];
496 -
771 +
772 + // RSS elements to Atom elements
773 + $this->normalize_element($item, 'description', $item, 'summary');
774 + $this->normalize_element($item['content'], 'encoded', $item, 'atom_content');
775 + $this->normalize_element($item, 'enclosure', $item, 'link_enclosure', 'normalize_enclosure');
776 +
777 + // Categories
778 + if ( isset($item['category#']) ) { // RSS 2.0 categories to dc:subject and Atom 1.0 categories
779 + $this->normalize_element($item, 'category', $item['dc'], 'subject', 'normalize_category');
780 + }
781 + elseif ( isset($item['dc']['subject#']) ) { // dc:subject to Atom 1.0 and RSS 2.0 categories
782 + $this->normalize_element($item['dc'], 'subject', $item, 'category', 'normalize_dc_subject');
783 + }
784 +
785 + // Normalized item timestamp
497 786 if ( $this->is_rss() == '1.0' and isset($item['dc']['date']) ) {
498 787 $epoch = @parse_w3cdtf($item['dc']['date']);
499 788 if ($epoch and $epoch > 0) {
500 789 $item['date_timestamp'] = $epoch;
@@ -505,14 +794,8 @@
505 794 if ($epoch > 0) {
506 795 $item['date_timestamp'] = $epoch;
507 796 }
508 797 }
509 -
510 - if ( is_array($item['categories']) ) {
511 - $item['category'] = $item['categories'][0];
512 - $item['dc']['subjects'] = $item['categories'];
513 - $item['dc']['subject'] = $item['category'];
514 - }
515 798
516 799 $this->items[$i] = $item;
517 800 }
518 801 }
@@ -650,9 +933,9 @@
650 933 }
651 934
652 935 function error ($errormsg, $lvl=E_USER_WARNING) {
653 936 // append PHP's error message if track_errors enabled
654 - if ( $php_errormsg ) {
937 + if ( isset($php_errormsg) ) {
655 938 $errormsg .= " ($php_errormsg)";
656 939 }
657 940 if ( MAGPIE_DEBUG ) {
658 941 trigger_error( $errormsg, $lvl);
@@ -667,19 +950,73 @@
667 950 } else {
668 951 $this->ERROR = $errormsg;
669 952 }
670 953 }
954 +
955 + // magic ID function for multiple elemenets.
956 + // can be called as static MagpieRSS::element_id()
957 + function element_id ($el, $counter) {
958 + return $el . (($counter > 1) ? '#'.$counter : '');
959 + }
671 960 } // end class RSS
672 961
673 962 function map_attrs($k, $v) {
674 963 return "$k=\"$v\"";
675 964 }
676 -# ---- cut here ----
677 965
966 +// patch to support medieval versions of PHP4.1.x,
967 +// courtesy, Ryan Currie, ryan@digibliss.com
968 +
969 +if (!function_exists('array_change_key_case')) {
970 + define("CASE_UPPER",1);
971 + define("CASE_LOWER",0);
972 +
973 +
974 + function array_change_key_case($array,$case=CASE_LOWER) {
975 + if ($case==CASE_LOWER) $cmd='strtolower';
976 + elseif ($case==CASE_UPPER) $cmd='strtoupper';
977 + foreach($array as $key=>$value) {
978 + $output[$cmd($key)]=$value;
979 + }
980 + return $output;
981 + }
982 +
983 +}
984 +
985 +################################################################################
986 +## WordPress: Load in Snoopy from wp-includes ##################################
987 +################################################################################
988 +
678 989 require_once( dirname(__FILE__) . '/class-snoopy.php');
679 990
680 -# -- UPDATED from rss_fetch.inc: fetch_rss, error, debug, magpie_error
681 -# --- cut here ---
991 +################################################################################
992 +## rss_fetch.inc: from MagpieRSS 0.8a ##########################################
993 +################################################################################
994 +
995 +/*=======================================================================*\
996 + Function: fetch_rss:
997 + Purpose: return RSS object for the give url
998 + maintain the cache
999 + Input: url of RSS file
1000 + Output: parsed RSS object (see rss_parse.inc)
1001 +
1002 + NOTES ON CACHEING:
1003 + If caching is on (MAGPIE_CACHE_ON) fetch_rss will first check the cache.
1004 +
1005 + NOTES ON RETRIEVING REMOTE FILES:
1006 + If conditional gets are on (MAGPIE_CONDITIONAL_GET_ON) fetch_rss will
1007 + return a cached object, and touch the cache object upon recieving a
1008 + 304.
1009 +
1010 + NOTES ON FAILED REQUESTS:
1011 + If there is an HTTP error while fetching an RSS object, the cached
1012 + version will be return, if it exists (and if MAGPIE_CACHE_FRESH_ONLY is off)
1013 +\*=======================================================================*/
1014 +
1015 +define('MAGPIE_VERSION', '0.7');
1016 +
1017 +$MAGPIE_ERROR = "";
1018 +
682 1019 function fetch_rss ($url) {
683 1020 // initialize constants
684 1021 init();
685 1022
@@ -732,9 +1069,9 @@
732 1069 // if object cached, and cache is fresh, return cached obj
733 1070 if ( $cache_status == 'HIT' ) {
734 1071 $rss = $cache->get( $cache_key );
735 1072 if ( isset($rss) and $rss ) {
736 - // should be cache age
1073 + // should be cache age
737 1074 $rss->from_cache = 1;
738 1075 if ( MAGPIE_DEBUG > 1) {
739 1076 debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
740 1077 }
@@ -849,12 +1186,9 @@
849 1186 }
850 1187
851 1188 return $MAGPIE_ERROR;
852 1189 }
853 -# --- cut here ---
854 1190
855 -# UPDATED FROM: rss_fetch.inc: _fetch_remote_file, _response_to_rss, init
856 -# --- cut here ---
857 1191 /*=======================================================================*\
858 1192 Function: _fetch_remote_file
859 1193 Purpose: retrieve an arbitrary remote file
860 1194 Input: url of the remote file
@@ -951,13 +1285,9 @@
951 1285 define('MAGPIE_CACHE_FRESH_ONLY', false);
952 1286 }
953 1287
954 1288 if ( !defined('MAGPIE_OUTPUT_ENCODING') ) {
955 - # WORDPRESS MODIFICATION: use whatever charset the blog is using
956 - # --- cut here ---
957 - $wp_encoding = get_settings('blog_charset');
958 - define('MAGPIE_OUTPUT_ENCODING', ($wp_encoding?$wp_encoding:'ISO-8859-1'));
959 - # --- cut here ---
1289 + define('MAGPIE_OUTPUT_ENCODING', 'ISO-8859-1');
960 1290 }
961 1291
962 1292 if ( !defined('MAGPIE_INPUT_ENCODING') ) {
963 1293 define('MAGPIE_INPUT_ENCODING', null);
@@ -971,13 +1301,10 @@
971 1301 define('MAGPIE_DEBUG', 0);
972 1302 }
973 1303
974 1304 if ( !defined('MAGPIE_USER_AGENT') ) {
975 - # WORDPRESS MODIFICATION: send WordPress as user-agent
976 - # --- cut here ---
977 - $ua = 'WordPress/'. $wp_version . ' (+http://www.wordpress.org';
978 - # --- cut here ---
979 -
1305 + $ua = 'MagpieRSS/'. MAGPIE_VERSION . ' (+http://magpierss.sf.net';
1306 +
980 1307 if ( MAGPIE_CACHE_ON ) {
981 1308 $ua = $ua . ')';
982 1309 }
983 1310 else {
@@ -995,36 +1322,75 @@
995 1322 if ( !defined('MAGPIE_USE_GZIP') ) {
996 1323 define('MAGPIE_USE_GZIP', true);
997 1324 }
998 1325 }
999 -# --- cut here ---
1000 1326
1327 +// NOTE: the following code should really be in Snoopy, or at least
1328 +// somewhere other then rss_fetch!
1329 +
1330 +/*=======================================================================*\
1331 + HTTP STATUS CODE PREDICATES
1332 + These functions attempt to classify an HTTP status code
1333 + based on RFC 2616 and RFC 2518.
1334 +
1335 + All of them take an HTTP status code as input, and return true or false
1336 +
1337 + All this code is adapted from LWP's HTTP::Status.
1338 +\*=======================================================================*/
1339 +
1340 +
1341 +/*=======================================================================*\
1342 + Function: is_info
1343 + Purpose: return true if Informational status code
1344 +\*=======================================================================*/
1001 1345 function is_info ($sc) {
1002 - return $sc >= 100 && $sc < 200;
1346 + return $sc >= 100 && $sc < 200;
1003 1347 }
1004 1348
1349 +/*=======================================================================*\
1350 + Function: is_success
1351 + Purpose: return true if Successful status code
1352 +\*=======================================================================*/
1005 1353 function is_success ($sc) {
1006 - return $sc >= 200 && $sc < 300;
1354 + return $sc >= 200 && $sc < 300;
1007 1355 }
1008 1356
1357 +/*=======================================================================*\
1358 + Function: is_redirect
1359 + Purpose: return true if Redirection status code
1360 +\*=======================================================================*/
1009 1361 function is_redirect ($sc) {
1010 - return $sc >= 300 && $sc < 400;
1362 + return $sc >= 300 && $sc < 400;
1011 1363 }
1012 1364
1365 +/*=======================================================================*\
1366 + Function: is_error
1367 + Purpose: return true if Error status code
1368 +\*=======================================================================*/
1013 1369 function is_error ($sc) {
1014 - return $sc >= 400 && $sc < 600;
1370 + return $sc >= 400 && $sc < 600;
1015 1371 }
1016 1372
1373 +/*=======================================================================*\
1374 + Function: is_client_error
1375 + Purpose: return true if Error status code, and its a client error
1376 +\*=======================================================================*/
1017 1377 function is_client_error ($sc) {
1018 - return $sc >= 400 && $sc < 500;
1378 + return $sc >= 400 && $sc < 500;
1019 1379 }
1020 1380
1381 +/*=======================================================================*\
1382 + Function: is_client_error
1383 + Purpose: return true if Error status code, and its a server error
1384 +\*=======================================================================*/
1021 1385 function is_server_error ($sc) {
1022 - return $sc >= 500 && $sc < 600;
1386 + return $sc >= 500 && $sc < 600;
1023 1387 }
1024 1388
1025 -# WORDPRESS-SPECIFIC: class RSSCache (modified to use WP database)
1026 -# --- cut here ---
1389 +################################################################################
1390 +## rss_cache.inc: from WordPress 1.5 ###########################################
1391 +################################################################################
1392 +
1027 1393 class RSSCache {
1028 1394 var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored
1029 1395 var $MAX_AGE = 43200; // when are files stale, default twelve hours
1030 1396 var $ERROR = ''; // accumulate error messages
@@ -1160,54 +1526,76 @@
1160 1526 $this->error("MagpieRSS [debug] $debugmsg", $lvl);
1161 1527 }
1162 1528 }
1163 1529 }
1164 -# --- cut here ---
1165 1530
1531 +################################################################################
1532 +## rss_utils.inc: from MagpieRSS 0.8a ##########################################
1533 +################################################################################
1534 +
1535 +/*======================================================================*\
1536 + Function: parse_w3cdtf
1537 + Purpose: parse a W3CDTF date into unix epoch
1538 +
1539 + NOTE: http://www.w3.org/TR/NOTE-datetime
1540 +\*======================================================================*/
1541 +
1166 1542 function parse_w3cdtf ( $date_str ) {
1167 -
1168 - # regex to match wc3dtf
1169 - $pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
1170 -
1171 - if ( preg_match( $pat, $date_str, $match ) ) {
1172 - list( $year, $month, $day, $hours, $minutes, $seconds) =
1173 - array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[6]);
1174 -
1175 - # calc epoch for current date assuming GMT
1176 - $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
1177 -
1178 - $offset = 0;
1179 - if ( $match[10] == 'Z' ) {
1180 - # zulu time, aka GMT
1181 - }
1182 - else {
1183 - list( $tz_mod, $tz_hour, $tz_min ) =
1184 - array( $match[8], $match[9], $match[10]);
1185 -
1186 - # zero out the variables
1187 - if ( ! $tz_hour ) { $tz_hour = 0; }
1188 - if ( ! $tz_min ) { $tz_min = 0; }
1189 -
1190 - $offset_secs = (($tz_hour*60)+$tz_min)*60;
1191 -
1192 - # is timezone ahead of GMT? then subtract offset
1193 - #
1194 - if ( $tz_mod == '+' ) {
1195 - $offset_secs = $offset_secs * -1;
1196 - }
1197 -
1198 - $offset = $offset_secs;
1199 - }
1200 - $epoch = $epoch + $offset;
1201 - return $epoch;
1202 - }
1203 - else {
1204 - return -1;
1205 - }
1543 +
1544 + # regex to match wc3dtf
1545 + $pat = "/^\s*(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.\d+)?)?(?:([-+])(\d{2}):?(\d{2})|(Z))?)?)?)?\s*\$/";
1546 +
1547 + if ( preg_match( $pat, $date_str, $match ) ) {
1548 + list( $year, $month, $day, $hours, $minutes, $seconds) =
1549 + array( $match[1], $match[3], $match[5], $match[7], $match[8], $match[9]);
1550 +
1551 + # W3C dates can omit the time, the day of the month, or even the month.
1552 + # Fill in any blanks using information from the present moment. --CWJ
1553 + $default['hr'] = (int) gmdate('H');
1554 + $default['day'] = (int) gmdate('d');
1555 + $default['month'] = (int) gmdate('m');
1556 +
1557 + if (is_null($hours)) : $hours = $default['hr']; $minutes = 0; $seconds = 0; endif;
1558 + if (is_null($day)) : $day = $default['day']; endif;
1559 + if (is_null($month)) : $month = $default['month']; endif;
1560 +
1561 + # calc epoch for current date assuming GMT
1562 + $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
1563 +
1564 + $offset = 0;
1565 + if ( $match[14] == 'Z' ) {
1566 + # zulu time, aka GMT
1567 + }
1568 + else {
1569 + list( $tz_mod, $tz_hour, $tz_min ) =
1570 + array( $match[12], $match[13], $match[14]);
1571 +
1572 + # zero out the variables
1573 + if ( ! $tz_hour ) { $tz_hour = 0; }
1574 + if ( ! $tz_min ) { $tz_min = 0; }
1575 +
1576 + $offset_secs = (($tz_hour*60)+$tz_min)*60;
1577 +
1578 + # is timezone ahead of GMT? then subtract offset
1579 + #
1580 + if ( $tz_mod == '+' ) {
1581 + $offset_secs = $offset_secs * -1;
1582 + }
1583 +
1584 + $offset = $offset_secs;
1585 + }
1586 + $epoch = $epoch + $offset;
1587 + return $epoch;
1588 + }
1589 + else {
1590 + return -1;
1591 + }
1206 1592 }
1207 1593
1208 -# WORDPRESS-SPECIFIC: wp_rss (), get_rss ()
1209 -# --- cut here ---
1594 +################################################################################
1595 +## WordPress: wp_rss(), get_rss() ##############################################
1596 +################################################################################
1597 +
1210 1598 function wp_rss ($url, $num) {
1211 1599 //ini_set("display_errors", false); uncomment to suppress php errors thrown if the feed is not returned.
1212 1600 $num_items = $num;
1213 1601 $rss = fetch_rss($url);
@@ -1243,6 +1631,5 @@
1243 1631 } else {
1244 1632 return false;
1245 1633 }
1246 1634 }
1247 -# --- cut here ---
1248 1635 ?>