PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-includes/rss.php

https://github.com/schr/wordpress
PHP | 922 lines | 647 code | 121 blank | 154 comment | 161 complexity | ee17c10e42666260cd9384522bb6a896 MD5 | raw file
  1. <?php
  2. /**
  3. * MagpieRSS: a simple RSS integration tool
  4. *
  5. * A compiled file for RSS syndication
  6. *
  7. * @author Kellan Elliott-McCrea <kellan@protest.net>
  8. * @version 0.51
  9. * @license GPL
  10. *
  11. * @package External
  12. * @subpackage MagpieRSS
  13. */
  14. /*
  15. * Hook to use another RSS object instead of MagpieRSS
  16. */
  17. do_action('load_feed_engine');
  18. /** RSS feed constant. */
  19. define('RSS', 'RSS');
  20. define('ATOM', 'Atom');
  21. define('MAGPIE_USER_AGENT', 'WordPress/' . $GLOBALS['wp_version']);
  22. class MagpieRSS {
  23. var $parser;
  24. var $current_item = array(); // item currently being parsed
  25. var $items = array(); // collection of parsed items
  26. var $channel = array(); // hash of channel fields
  27. var $textinput = array();
  28. var $image = array();
  29. var $feed_type;
  30. var $feed_version;
  31. // parser variables
  32. var $stack = array(); // parser stack
  33. var $inchannel = false;
  34. var $initem = false;
  35. var $incontent = false; // if in Atom <content mode="xml"> field
  36. var $intextinput = false;
  37. var $inimage = false;
  38. var $current_field = '';
  39. var $current_namespace = false;
  40. //var $ERROR = "";
  41. var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
  42. function MagpieRSS ($source) {
  43. # if PHP xml isn't compiled in, die
  44. #
  45. if ( !function_exists('xml_parser_create') )
  46. trigger_error( "Failed to load PHP's XML Extension. http://www.php.net/manual/en/ref.xml.php" );
  47. $parser = @xml_parser_create();
  48. if ( !is_resource($parser) )
  49. trigger_error( "Failed to create an instance of PHP's XML parser. http://www.php.net/manual/en/ref.xml.php");
  50. $this->parser = $parser;
  51. # pass in parser, and a reference to this object
  52. # setup handlers
  53. #
  54. xml_set_object( $this->parser, $this );
  55. xml_set_element_handler($this->parser,
  56. 'feed_start_element', 'feed_end_element' );
  57. xml_set_character_data_handler( $this->parser, 'feed_cdata' );
  58. $status = xml_parse( $this->parser, $source );
  59. if (! $status ) {
  60. $errorcode = xml_get_error_code( $this->parser );
  61. if ( $errorcode != XML_ERROR_NONE ) {
  62. $xml_error = xml_error_string( $errorcode );
  63. $error_line = xml_get_current_line_number($this->parser);
  64. $error_col = xml_get_current_column_number($this->parser);
  65. $errormsg = "$xml_error at line $error_line, column $error_col";
  66. $this->error( $errormsg );
  67. }
  68. }
  69. xml_parser_free( $this->parser );
  70. $this->normalize();
  71. }
  72. function feed_start_element($p, $element, &$attrs) {
  73. $el = $element = strtolower($element);
  74. $attrs = array_change_key_case($attrs, CASE_LOWER);
  75. // check for a namespace, and split if found
  76. $ns = false;
  77. if ( strpos( $element, ':' ) ) {
  78. list($ns, $el) = split( ':', $element, 2);
  79. }
  80. if ( $ns and $ns != 'rdf' ) {
  81. $this->current_namespace = $ns;
  82. }
  83. # if feed type isn't set, then this is first element of feed
  84. # identify feed from root element
  85. #
  86. if (!isset($this->feed_type) ) {
  87. if ( $el == 'rdf' ) {
  88. $this->feed_type = RSS;
  89. $this->feed_version = '1.0';
  90. }
  91. elseif ( $el == 'rss' ) {
  92. $this->feed_type = RSS;
  93. $this->feed_version = $attrs['version'];
  94. }
  95. elseif ( $el == 'feed' ) {
  96. $this->feed_type = ATOM;
  97. $this->feed_version = $attrs['version'];
  98. $this->inchannel = true;
  99. }
  100. return;
  101. }
  102. if ( $el == 'channel' )
  103. {
  104. $this->inchannel = true;
  105. }
  106. elseif ($el == 'item' or $el == 'entry' )
  107. {
  108. $this->initem = true;
  109. if ( isset($attrs['rdf:about']) ) {
  110. $this->current_item['about'] = $attrs['rdf:about'];
  111. }
  112. }
  113. // if we're in the default namespace of an RSS feed,
  114. // record textinput or image fields
  115. elseif (
  116. $this->feed_type == RSS and
  117. $this->current_namespace == '' and
  118. $el == 'textinput' )
  119. {
  120. $this->intextinput = true;
  121. }
  122. elseif (
  123. $this->feed_type == RSS and
  124. $this->current_namespace == '' and
  125. $el == 'image' )
  126. {
  127. $this->inimage = true;
  128. }
  129. # handle atom content constructs
  130. elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
  131. {
  132. // avoid clashing w/ RSS mod_content
  133. if ($el == 'content' ) {
  134. $el = 'atom_content';
  135. }
  136. $this->incontent = $el;
  137. }
  138. // if inside an Atom content construct (e.g. content or summary) field treat tags as text
  139. elseif ($this->feed_type == ATOM and $this->incontent )
  140. {
  141. // if tags are inlined, then flatten
  142. $attrs_str = join(' ',
  143. array_map('map_attrs',
  144. array_keys($attrs),
  145. array_values($attrs) ) );
  146. $this->append_content( "<$element $attrs_str>" );
  147. array_unshift( $this->stack, $el );
  148. }
  149. // Atom support many links per containging element.
  150. // Magpie treats link elements of type rel='alternate'
  151. // as being equivalent to RSS's simple link element.
  152. //
  153. elseif ($this->feed_type == ATOM and $el == 'link' )
  154. {
  155. if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
  156. {
  157. $link_el = 'link';
  158. }
  159. else {
  160. $link_el = 'link_' . $attrs['rel'];
  161. }
  162. $this->append($link_el, $attrs['href']);
  163. }
  164. // set stack[0] to current element
  165. else {
  166. array_unshift($this->stack, $el);
  167. }
  168. }
  169. function feed_cdata ($p, $text) {
  170. if ($this->feed_type == ATOM and $this->incontent)
  171. {
  172. $this->append_content( $text );
  173. }
  174. else {
  175. $current_el = join('_', array_reverse($this->stack));
  176. $this->append($current_el, $text);
  177. }
  178. }
  179. function feed_end_element ($p, $el) {
  180. $el = strtolower($el);
  181. if ( $el == 'item' or $el == 'entry' )
  182. {
  183. $this->items[] = $this->current_item;
  184. $this->current_item = array();
  185. $this->initem = false;
  186. }
  187. elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
  188. {
  189. $this->intextinput = false;
  190. }
  191. elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
  192. {
  193. $this->inimage = false;
  194. }
  195. elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
  196. {
  197. $this->incontent = false;
  198. }
  199. elseif ($el == 'channel' or $el == 'feed' )
  200. {
  201. $this->inchannel = false;
  202. }
  203. elseif ($this->feed_type == ATOM and $this->incontent ) {
  204. // balance tags properly
  205. // note: i don't think this is actually neccessary
  206. if ( $this->stack[0] == $el )
  207. {
  208. $this->append_content("</$el>");
  209. }
  210. else {
  211. $this->append_content("<$el />");
  212. }
  213. array_shift( $this->stack );
  214. }
  215. else {
  216. array_shift( $this->stack );
  217. }
  218. $this->current_namespace = false;
  219. }
  220. function concat (&$str1, $str2="") {
  221. if (!isset($str1) ) {
  222. $str1="";
  223. }
  224. $str1 .= $str2;
  225. }
  226. function append_content($text) {
  227. if ( $this->initem ) {
  228. $this->concat( $this->current_item[ $this->incontent ], $text );
  229. }
  230. elseif ( $this->inchannel ) {
  231. $this->concat( $this->channel[ $this->incontent ], $text );
  232. }
  233. }
  234. // smart append - field and namespace aware
  235. function append($el, $text) {
  236. if (!$el) {
  237. return;
  238. }
  239. if ( $this->current_namespace )
  240. {
  241. if ( $this->initem ) {
  242. $this->concat(
  243. $this->current_item[ $this->current_namespace ][ $el ], $text);
  244. }
  245. elseif ($this->inchannel) {
  246. $this->concat(
  247. $this->channel[ $this->current_namespace][ $el ], $text );
  248. }
  249. elseif ($this->intextinput) {
  250. $this->concat(
  251. $this->textinput[ $this->current_namespace][ $el ], $text );
  252. }
  253. elseif ($this->inimage) {
  254. $this->concat(
  255. $this->image[ $this->current_namespace ][ $el ], $text );
  256. }
  257. }
  258. else {
  259. if ( $this->initem ) {
  260. $this->concat(
  261. $this->current_item[ $el ], $text);
  262. }
  263. elseif ($this->intextinput) {
  264. $this->concat(
  265. $this->textinput[ $el ], $text );
  266. }
  267. elseif ($this->inimage) {
  268. $this->concat(
  269. $this->image[ $el ], $text );
  270. }
  271. elseif ($this->inchannel) {
  272. $this->concat(
  273. $this->channel[ $el ], $text );
  274. }
  275. }
  276. }
  277. function normalize () {
  278. // if atom populate rss fields
  279. if ( $this->is_atom() ) {
  280. $this->channel['descripton'] = $this->channel['tagline'];
  281. for ( $i = 0; $i < count($this->items); $i++) {
  282. $item = $this->items[$i];
  283. if ( isset($item['summary']) )
  284. $item['description'] = $item['summary'];
  285. if ( isset($item['atom_content']))
  286. $item['content']['encoded'] = $item['atom_content'];
  287. $this->items[$i] = $item;
  288. }
  289. }
  290. elseif ( $this->is_rss() ) {
  291. $this->channel['tagline'] = $this->channel['description'];
  292. for ( $i = 0; $i < count($this->items); $i++) {
  293. $item = $this->items[$i];
  294. if ( isset($item['description']))
  295. $item['summary'] = $item['description'];
  296. if ( isset($item['content']['encoded'] ) )
  297. $item['atom_content'] = $item['content']['encoded'];
  298. $this->items[$i] = $item;
  299. }
  300. }
  301. }
  302. function is_rss () {
  303. if ( $this->feed_type == RSS ) {
  304. return $this->feed_version;
  305. }
  306. else {
  307. return false;
  308. }
  309. }
  310. function is_atom() {
  311. if ( $this->feed_type == ATOM ) {
  312. return $this->feed_version;
  313. }
  314. else {
  315. return false;
  316. }
  317. }
  318. function map_attrs($k, $v) {
  319. return "$k=\"$v\"";
  320. }
  321. function error( $errormsg, $lvl = E_USER_WARNING ) {
  322. // append PHP's error message if track_errors enabled
  323. if ( isset($php_errormsg) ) {
  324. $errormsg .= " ($php_errormsg)";
  325. }
  326. if ( MAGPIE_DEBUG ) {
  327. trigger_error( $errormsg, $lvl);
  328. } else {
  329. error_log( $errormsg, 0);
  330. }
  331. }
  332. }
  333. if ( !function_exists('fetch_rss') ) :
  334. /**
  335. * Build Magpie object based on RSS from URL.
  336. *
  337. * @since unknown
  338. * @package External
  339. * @subpackage MagpieRSS
  340. *
  341. * @param string $url URL to retrieve feed
  342. * @return bool|MagpieRSS false on failure or MagpieRSS object on success.
  343. */
  344. function fetch_rss ($url) {
  345. // initialize constants
  346. init();
  347. if ( !isset($url) ) {
  348. // error("fetch_rss called without a url");
  349. return false;
  350. }
  351. // if cache is disabled
  352. if ( !MAGPIE_CACHE_ON ) {
  353. // fetch file, and parse it
  354. $resp = _fetch_remote_file( $url );
  355. if ( is_success( $resp->status ) ) {
  356. return _response_to_rss( $resp );
  357. }
  358. else {
  359. // error("Failed to fetch $url and cache is off");
  360. return false;
  361. }
  362. }
  363. // else cache is ON
  364. else {
  365. // Flow
  366. // 1. check cache
  367. // 2. if there is a hit, make sure its fresh
  368. // 3. if cached obj fails freshness check, fetch remote
  369. // 4. if remote fails, return stale object, or error
  370. $cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
  371. if (MAGPIE_DEBUG and $cache->ERROR) {
  372. debug($cache->ERROR, E_USER_WARNING);
  373. }
  374. $cache_status = 0; // response of check_cache
  375. $request_headers = array(); // HTTP headers to send with fetch
  376. $rss = 0; // parsed RSS object
  377. $errormsg = 0; // errors, if any
  378. if (!$cache->ERROR) {
  379. // return cache HIT, MISS, or STALE
  380. $cache_status = $cache->check_cache( $url );
  381. }
  382. // if object cached, and cache is fresh, return cached obj
  383. if ( $cache_status == 'HIT' ) {
  384. $rss = $cache->get( $url );
  385. if ( isset($rss) and $rss ) {
  386. $rss->from_cache = 1;
  387. if ( MAGPIE_DEBUG > 1) {
  388. debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
  389. }
  390. return $rss;
  391. }
  392. }
  393. // else attempt a conditional get
  394. // setup headers
  395. if ( $cache_status == 'STALE' ) {
  396. $rss = $cache->get( $url );
  397. if ( isset($rss->etag) and $rss->last_modified ) {
  398. $request_headers['If-None-Match'] = $rss->etag;
  399. $request_headers['If-Last-Modified'] = $rss->last_modified;
  400. }
  401. }
  402. $resp = _fetch_remote_file( $url, $request_headers );
  403. if (isset($resp) and $resp) {
  404. if ($resp->status == '304' ) {
  405. // we have the most current copy
  406. if ( MAGPIE_DEBUG > 1) {
  407. debug("Got 304 for $url");
  408. }
  409. // reset cache on 304 (at minutillo insistent prodding)
  410. $cache->set($url, $rss);
  411. return $rss;
  412. }
  413. elseif ( is_success( $resp->status ) ) {
  414. $rss = _response_to_rss( $resp );
  415. if ( $rss ) {
  416. if (MAGPIE_DEBUG > 1) {
  417. debug("Fetch successful");
  418. }
  419. // add object to cache
  420. $cache->set( $url, $rss );
  421. return $rss;
  422. }
  423. }
  424. else {
  425. $errormsg = "Failed to fetch $url. ";
  426. if ( $resp->error ) {
  427. # compensate for Snoopy's annoying habbit to tacking
  428. # on '\n'
  429. $http_error = substr($resp->error, 0, -2);
  430. $errormsg .= "(HTTP Error: $http_error)";
  431. }
  432. else {
  433. $errormsg .= "(HTTP Response: " . $resp->response_code .')';
  434. }
  435. }
  436. }
  437. else {
  438. $errormsg = "Unable to retrieve RSS file for unknown reasons.";
  439. }
  440. // else fetch failed
  441. // attempt to return cached object
  442. if ($rss) {
  443. if ( MAGPIE_DEBUG ) {
  444. debug("Returning STALE object for $url");
  445. }
  446. return $rss;
  447. }
  448. // else we totally failed
  449. // error( $errormsg );
  450. return false;
  451. } // end if ( !MAGPIE_CACHE_ON ) {
  452. } // end fetch_rss()
  453. endif;
  454. /**
  455. * Retrieve URL headers and content using WP HTTP Request API.
  456. *
  457. * @since unknown
  458. * @package External
  459. * @subpackage MagpieRSS
  460. *
  461. * @param string $url URL to retrieve
  462. * @param array $headers Optional. Headers to send to the URL.
  463. * @return Snoopy style response
  464. */
  465. function _fetch_remote_file ($url, $headers = "" ) {
  466. $resp = wp_remote_request($url, array('headers' => $headers, 'timeout' => MAGPIE_FETCH_TIME_OUT));
  467. if ( is_wp_error($resp) ) {
  468. $error = array_shift($resp->errors);
  469. $resp = new stdClass;
  470. $resp->status = 500;
  471. $resp->response_code = 500;
  472. $resp->error = $error[0] . "\n"; //\n = Snoopy compatibility
  473. return $resp;
  474. }
  475. $response = new stdClass;
  476. $response->status = $resp['response']['code'];
  477. $response->response_code = $resp['response']['code'];
  478. $response->headers = $resp['headers'];
  479. $response->results = $resp['body'];
  480. return $response;
  481. }
  482. /**
  483. * Retrieve
  484. *
  485. * @since unknown
  486. * @package External
  487. * @subpackage MagpieRSS
  488. *
  489. * @param unknown_type $resp
  490. * @return unknown
  491. */
  492. function _response_to_rss ($resp) {
  493. $rss = new MagpieRSS( $resp->results );
  494. // if RSS parsed successfully
  495. if ( $rss && (!isset($rss->ERROR) || !$rss->ERROR) ) {
  496. // find Etag, and Last-Modified
  497. foreach( (array) $resp->headers as $h) {
  498. // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
  499. if (strpos($h, ": ")) {
  500. list($field, $val) = explode(": ", $h, 2);
  501. }
  502. else {
  503. $field = $h;
  504. $val = "";
  505. }
  506. if ( $field == 'ETag' ) {
  507. $rss->etag = $val;
  508. }
  509. if ( $field == 'Last-Modified' ) {
  510. $rss->last_modified = $val;
  511. }
  512. }
  513. return $rss;
  514. } // else construct error message
  515. else {
  516. $errormsg = "Failed to parse RSS file.";
  517. if ($rss) {
  518. $errormsg .= " (" . $rss->ERROR . ")";
  519. }
  520. // error($errormsg);
  521. return false;
  522. } // end if ($rss and !$rss->error)
  523. }
  524. /**
  525. * Setup constants with default values, unless user overrides.
  526. *
  527. * @since unknown
  528. * @package External
  529. * @subpackage MagpieRSS
  530. */
  531. function init () {
  532. if ( defined('MAGPIE_INITALIZED') ) {
  533. return;
  534. }
  535. else {
  536. define('MAGPIE_INITALIZED', 1);
  537. }
  538. if ( !defined('MAGPIE_CACHE_ON') ) {
  539. define('MAGPIE_CACHE_ON', 1);
  540. }
  541. if ( !defined('MAGPIE_CACHE_DIR') ) {
  542. define('MAGPIE_CACHE_DIR', './cache');
  543. }
  544. if ( !defined('MAGPIE_CACHE_AGE') ) {
  545. define('MAGPIE_CACHE_AGE', 60*60); // one hour
  546. }
  547. if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
  548. define('MAGPIE_CACHE_FRESH_ONLY', 0);
  549. }
  550. if ( !defined('MAGPIE_DEBUG') ) {
  551. define('MAGPIE_DEBUG', 0);
  552. }
  553. if ( !defined('MAGPIE_USER_AGENT') ) {
  554. $ua = 'WordPress/' . $GLOBALS['wp_version'];
  555. if ( MAGPIE_CACHE_ON ) {
  556. $ua = $ua . ')';
  557. }
  558. else {
  559. $ua = $ua . '; No cache)';
  560. }
  561. define('MAGPIE_USER_AGENT', $ua);
  562. }
  563. if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
  564. define('MAGPIE_FETCH_TIME_OUT', 2); // 2 second timeout
  565. }
  566. // use gzip encoding to fetch rss files if supported?
  567. if ( !defined('MAGPIE_USE_GZIP') ) {
  568. define('MAGPIE_USE_GZIP', true);
  569. }
  570. }
  571. function is_info ($sc) {
  572. return $sc >= 100 && $sc < 200;
  573. }
  574. function is_success ($sc) {
  575. return $sc >= 200 && $sc < 300;
  576. }
  577. function is_redirect ($sc) {
  578. return $sc >= 300 && $sc < 400;
  579. }
  580. function is_error ($sc) {
  581. return $sc >= 400 && $sc < 600;
  582. }
  583. function is_client_error ($sc) {
  584. return $sc >= 400 && $sc < 500;
  585. }
  586. function is_server_error ($sc) {
  587. return $sc >= 500 && $sc < 600;
  588. }
  589. class RSSCache {
  590. var $BASE_CACHE; // where the cache files are stored
  591. var $MAX_AGE = 43200; // when are files stale, default twelve hours
  592. var $ERROR = ''; // accumulate error messages
  593. function RSSCache ($base='', $age='') {
  594. $this->BASE_CACHE = WP_CONTENT_DIR . '/cache';
  595. if ( $base ) {
  596. $this->BASE_CACHE = $base;
  597. }
  598. if ( $age ) {
  599. $this->MAX_AGE = $age;
  600. }
  601. }
  602. /*=======================================================================*\
  603. Function: set
  604. Purpose: add an item to the cache, keyed on url
  605. Input: url from wich the rss file was fetched
  606. Output: true on sucess
  607. \*=======================================================================*/
  608. function set ($url, $rss) {
  609. global $wpdb;
  610. $cache_option = 'rss_' . $this->file_name( $url );
  611. set_transient($cache_option, $rss, $this->MAX_AGE);
  612. return $cache_option;
  613. }
  614. /*=======================================================================*\
  615. Function: get
  616. Purpose: fetch an item from the cache
  617. Input: url from wich the rss file was fetched
  618. Output: cached object on HIT, false on MISS
  619. \*=======================================================================*/
  620. function get ($url) {
  621. $this->ERROR = "";
  622. $cache_option = 'rss_' . $this->file_name( $url );
  623. if ( ! $rss = get_transient( $cache_option ) ) {
  624. $this->debug(
  625. "Cache doesn't contain: $url (cache option: $cache_option)"
  626. );
  627. return 0;
  628. }
  629. return $rss;
  630. }
  631. /*=======================================================================*\
  632. Function: check_cache
  633. Purpose: check a url for membership in the cache
  634. and whether the object is older then MAX_AGE (ie. STALE)
  635. Input: url from wich the rss file was fetched
  636. Output: cached object on HIT, false on MISS
  637. \*=======================================================================*/
  638. function check_cache ( $url ) {
  639. $this->ERROR = "";
  640. $cache_option = 'rss_' . $this->file_name( $url );
  641. if ( get_transient($cache_option) ) {
  642. // object exists and is current
  643. return 'HIT';
  644. } else {
  645. // object does not exist
  646. return 'MISS';
  647. }
  648. }
  649. /*=======================================================================*\
  650. Function: serialize
  651. \*=======================================================================*/
  652. function serialize ( $rss ) {
  653. return serialize( $rss );
  654. }
  655. /*=======================================================================*\
  656. Function: unserialize
  657. \*=======================================================================*/
  658. function unserialize ( $data ) {
  659. return unserialize( $data );
  660. }
  661. /*=======================================================================*\
  662. Function: file_name
  663. Purpose: map url to location in cache
  664. Input: url from wich the rss file was fetched
  665. Output: a file name
  666. \*=======================================================================*/
  667. function file_name ($url) {
  668. return md5( $url );
  669. }
  670. /*=======================================================================*\
  671. Function: error
  672. Purpose: register error
  673. \*=======================================================================*/
  674. function error ($errormsg, $lvl=E_USER_WARNING) {
  675. // append PHP's error message if track_errors enabled
  676. if ( isset($php_errormsg) ) {
  677. $errormsg .= " ($php_errormsg)";
  678. }
  679. $this->ERROR = $errormsg;
  680. if ( MAGPIE_DEBUG ) {
  681. trigger_error( $errormsg, $lvl);
  682. }
  683. else {
  684. error_log( $errormsg, 0);
  685. }
  686. }
  687. function debug ($debugmsg, $lvl=E_USER_NOTICE) {
  688. if ( MAGPIE_DEBUG ) {
  689. $this->error("MagpieRSS [debug] $debugmsg", $lvl);
  690. }
  691. }
  692. }
  693. if ( !function_exists('parse_w3cdtf') ) :
  694. function parse_w3cdtf ( $date_str ) {
  695. # regex to match wc3dtf
  696. $pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
  697. if ( preg_match( $pat, $date_str, $match ) ) {
  698. list( $year, $month, $day, $hours, $minutes, $seconds) =
  699. array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]);
  700. # calc epoch for current date assuming GMT
  701. $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
  702. $offset = 0;
  703. if ( $match[11] == 'Z' ) {
  704. # zulu time, aka GMT
  705. }
  706. else {
  707. list( $tz_mod, $tz_hour, $tz_min ) =
  708. array( $match[8], $match[9], $match[10]);
  709. # zero out the variables
  710. if ( ! $tz_hour ) { $tz_hour = 0; }
  711. if ( ! $tz_min ) { $tz_min = 0; }
  712. $offset_secs = (($tz_hour*60)+$tz_min)*60;
  713. # is timezone ahead of GMT? then subtract offset
  714. #
  715. if ( $tz_mod == '+' ) {
  716. $offset_secs = $offset_secs * -1;
  717. }
  718. $offset = $offset_secs;
  719. }
  720. $epoch = $epoch + $offset;
  721. return $epoch;
  722. }
  723. else {
  724. return -1;
  725. }
  726. }
  727. endif;
  728. if ( !function_exists('wp_rss') ) :
  729. /**
  730. * Display all RSS items in a HTML ordered list.
  731. *
  732. * @since unknown
  733. * @package External
  734. * @subpackage MagpieRSS
  735. *
  736. * @param string $url URL of feed to display. Will not auto sense feed URL.
  737. * @param int $num_items Optional. Number of items to display, default is all.
  738. */
  739. function wp_rss( $url, $num_items = -1 ) {
  740. if ( $rss = fetch_rss( $url ) ) {
  741. echo '<ul>';
  742. if ( $num_items !== -1 ) {
  743. $rss->items = array_slice( $rss->items, 0, $num_items );
  744. }
  745. foreach ( (array) $rss->items as $item ) {
  746. printf(
  747. '<li><a href="%1$s" title="%2$s">%3$s</a></li>',
  748. clean_url( $item['link'] ),
  749. attribute_escape( strip_tags( $item['description'] ) ),
  750. htmlentities( $item['title'] )
  751. );
  752. }
  753. echo '</ul>';
  754. } else {
  755. _e( 'An error has occurred, which probably means the feed is down. Try again later.' );
  756. }
  757. }
  758. endif;
  759. if ( !function_exists('get_rss') ) :
  760. /**
  761. * Display RSS items in HTML list items.
  762. *
  763. * You have to specify which HTML list you want, either ordered or unordered
  764. * before using the function. You also have to specify how many items you wish
  765. * to display. You can't display all of them like you can with wp_rss()
  766. * function.
  767. *
  768. * @since unknown
  769. * @package External
  770. * @subpackage MagpieRSS
  771. *
  772. * @param string $url URL of feed to display. Will not auto sense feed URL.
  773. * @param int $num_items Optional. Number of items to display, default is all.
  774. * @return bool False on failure.
  775. */
  776. function get_rss ($url, $num_items = 5) { // Like get posts, but for RSS
  777. $rss = fetch_rss($url);
  778. if ( $rss ) {
  779. $rss->items = array_slice($rss->items, 0, $num_items);
  780. foreach ( (array) $rss->items as $item ) {
  781. echo "<li>\n";
  782. echo "<a href='$item[link]' title='$item[description]'>";
  783. echo htmlentities($item['title']);
  784. echo "</a><br />\n";
  785. echo "</li>\n";
  786. }
  787. } else {
  788. return false;
  789. }
  790. }
  791. endif;
  792. ?>