From e4426def6d30d301a3f2eb8f7270345d9781edfe Mon Sep 17 00:00:00 2001 From: donncha Date: Thu, 11 Jan 2007 15:57:00 +0000 Subject: WP Merge to rev 4721 git-svn-id: http://svn.automattic.com/wordpress-mu/trunk@840 7be80a69-a1ef-0310-a953-fb0f7c49ff36 --- wp-includes/category.php | 2 +- .../tinymce/plugins/inlinepopups/editor_plugin.js | 2 + wp-includes/js/tinymce/tiny_mce.js | 2 +- wp-includes/link-template.php | 12 +- wp-includes/rss.php | 1610 ++++++++------------ 5 files changed, 650 insertions(+), 978 deletions(-) (limited to 'wp-includes') diff --git a/wp-includes/category.php b/wp-includes/category.php index 6025a67..7f47161 100644 --- a/wp-includes/category.php +++ b/wp-includes/category.php @@ -294,7 +294,7 @@ function _pad_category_counts($type, &$categories) { } // Transfer the touched cells - foreach ( $cat_items as $id => $items ) + foreach ( (array) $cat_items as $id => $items ) if ( isset($cats[$id]) ) $cats[$id]->{'link' == $type ? 'link_count' : 'category_count'} = count($items); } diff --git a/wp-includes/js/tinymce/plugins/inlinepopups/editor_plugin.js b/wp-includes/js/tinymce/plugins/inlinepopups/editor_plugin.js index c005a9e..304e478 100644 --- a/wp-includes/js/tinymce/plugins/inlinepopups/editor_plugin.js +++ b/wp-includes/js/tinymce/plugins/inlinepopups/editor_plugin.js @@ -558,6 +558,8 @@ TinyMCE_Window.prototype.close = function() { e.parentNode.removeChild(e); mcWindows.setDocumentLock(false); + + tinyMCE.selectedInstance.getWin().focus(); }; TinyMCE_Window.prototype.onMouseMove = function(e) { diff --git a/wp-includes/js/tinymce/tiny_mce.js b/wp-includes/js/tinymce/tiny_mce.js index ecd78a2..aece8d1 100644 --- a/wp-includes/js/tinymce/tiny_mce.js +++ b/wp-includes/js/tinymce/tiny_mce.js @@ -2813,7 +2813,7 @@ TinyMCE_Control.prototype = { if (tinyMCE.isGecko && this.getSel().isCollapsed) { focusElm = tinyMCE.getParentElement(focusElm, 'A'); - if (focusElm) + if (focusElm && this.getRng(0).endOffset > 0 && this.getRng(0).endOffset != focusElm.innerHTML.length) this.selection.selectNode(focusElm, false); } diff --git a/wp-includes/link-template.php b/wp-includes/link-template.php index ef570f2..20b9132 100644 --- a/wp-includes/link-template.php +++ b/wp-includes/link-template.php @@ -296,7 +296,11 @@ function get_previous_post($in_same_cat = false, $excluded_categories = '') { $posts_in_ex_cats_sql = 'AND ID NOT IN (' . implode($posts_in_ex_cats, ',') . ')'; } - return @$wpdb->get_row("SELECT ID, post_title FROM $wpdb->posts $join WHERE post_date < '$current_post_date' AND post_type = 'post' AND post_status = 'publish' $posts_in_ex_cats_sql ORDER BY post_date DESC LIMIT 1"); + $join = apply_filters( 'get_previous_post_join', $join, $in_same_cat, $excluded_categories ); + $where = apply_filters( 'get_previous_post_where', "WHERE post_date < '$current_post_date' AND post_type = 'post' AND post_status = 'publish' $posts_in_ex_cats_sql", $in_same_cat, $excluded_categories ); + $sort = apply_filters( 'get_previous_post_sort', 'ORDER BY post_date DESC LIMIT 1' ); + + return @$wpdb->get_row("SELECT ID, post_title FROM $wpdb->posts $join $where $sort"); } function get_next_post($in_same_cat = false, $excluded_categories = '') { @@ -329,7 +333,11 @@ function get_next_post($in_same_cat = false, $excluded_categories = '') { $posts_in_ex_cats_sql = 'AND ID NOT IN (' . implode($posts_in_ex_cats, ',') . ')'; } - return @$wpdb->get_row("SELECT ID,post_title FROM $wpdb->posts $join WHERE post_date > '$current_post_date' AND post_type = 'post' AND post_status = 'publish' $posts_in_ex_cats_sql AND ID != $post->ID ORDER BY post_date ASC LIMIT 1"); + $join = apply_filters( 'get_next_post_join', $join, $in_same_cat, $excluded_categories ); + $where = apply_filters( 'get_next_post_where', "WHERE post_date > '$current_post_date' AND post_type = 'post' AND post_status = 'publish' $posts_in_ex_cats_sql AND ID != $post->ID", $in_same_cat, $excluded_categories ); + $sort = apply_filters( 'get_next_post_sort', 'ORDER BY post_date ASC LIMIT 1' ); + + return @$wpdb->get_row("SELECT ID, post_title FROM $wpdb->posts $join $where $sort"); } diff --git a/wp-includes/rss.php b/wp-includes/rss.php index e0c1d84..a42e7e0 100644 --- a/wp-includes/rss.php +++ b/wp-includes/rss.php @@ -1,1159 +1,788 @@ which is - * released under the GPL license. - * - * The lastest version of MagpieRSS can be obtained from: - * http://magpierss.sourceforge.net +/* + * Project: MagpieRSS: a simple RSS integration tool + * File: A compiled file for RSS syndication + * Author: Kellan Elliott-McCrea + * Version: 0.51 + * License: GPL */ -function fetch_rss($url) { - $url = apply_filters('fetch_rss_url', $url); +define('RSS', 'RSS'); +define('ATOM', 'Atom'); +define('MAGPIE_USER_AGENT', 'WordPressMU/' . $wp_version); - $feeder = new WP_Feeder(); +class MagpieRSS { + var $parser; + var $current_item = array(); // item currently being parsed + var $items = array(); // collection of parsed items + var $channel = array(); // hash of channel fields + var $textinput = array(); + var $image = array(); + var $feed_type; + var $feed_version; - $feed = $feeder->get($url); + // parser variables + var $stack = array(); // parser stack + var $inchannel = false; + var $initem = false; + var $incontent = false; // if in Atom field + var $intextinput = false; + var $inimage = false; + var $current_field = ''; + var $current_namespace = false; - $magpie = $feed->to_magpie(); + //var $ERROR = ""; - return $magpie; -} + var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright'); -class WP_Feeder { - var $url, $http_client, $last_fetch, $wp_object_cache, $cache; - var $redirects = 0; - var $max_redirects = 3; - var $cache_redirects = true; - - function WP_Feeder () { - global $wp_object_cache; - - if ( $wp_object_cache->cache_enabled ) { - $this->wp_object_cache = true; - } else { - $this->wp_object_cache = false; - $this->cache = new RSSCache(); - } - } + function MagpieRSS ($source) { - function get ($url) { - $cached = false; + # if PHP xml isn't compiled in, die + # + if ( !function_exists('xml_parser_create') ) + trigger_error( "Failed to load PHP's XML Extension. http://www.php.net/manual/en/ref.xml.php" ); - $feed = $this->cache_get($url); + $parser = @xml_parser_create(); - if ( is_object($feed) ) { - $cached = true; - } else { - unset($feed); + if ( !is_resource($parser) ) + trigger_error( "Failed to create an instance of PHP's XML parser. http://www.php.net/manual/en/ref.xml.php"); - $this->fetch($url); - $feed = new WP_Feed($this->http_client); - } + $this->parser = $parser; - // Handle redirects - if ( $feed->status >= 300 && $feed->status < 400 && $this->redirects < $this->max_redirects ) { - ++$this->redirects; + # pass in parser, and a reference to this object + # setup handlers + # + xml_set_object( $this->parser, $this ); + xml_set_element_handler($this->parser, + 'feed_start_element', 'feed_end_element' ); - if ( $this->cache_redirects && !$cached ) - $this->cache_set($url, $feed); + xml_set_character_data_handler( $this->parser, 'feed_cdata' ); - return $this->get($feed->redirect_location); - } + $status = xml_parse( $this->parser, $source ); - if ( !$cached ) - $this->cache_set($url, $feed); + if (! $status ) { + $errorcode = xml_get_error_code( $this->parser ); + if ( $errorcode != XML_ERROR_NONE ) { + $xml_error = xml_error_string( $errorcode ); + $error_line = xml_get_current_line_number($this->parser); + $error_col = xml_get_current_column_number($this->parser); + $errormsg = "$xml_error at line $error_line, column $error_col"; - return $feed; - } + $this->error( $errormsg ); + } + } - function fetch ($url) { - $this->last_fetch = $url; - $parts = parse_url($url); - $url = ($parts['path'] ? $parts['path'] : '/') . ($parts['query'] ? '?'.$parts['query'] : ''); - $this->http_client = new HttpClient('', 80); - $this->http_client->handle_redirects = false; - $this->http_client->host = $parts['host']; - $this->http_client->port = $parts['port'] ? $parts['port'] : 80; - $this->http_client->user_agent = 'WordPress ' . $GLOBALS['wp_version'] . ' Feed Client'; - $this->http_client->get($url); - } - - function cache_get ($url) { - if ( $this->wp_object_cache ) - return unserialize(wp_cache_get($url, 'rss')); + xml_parser_free( $this->parser ); - return $this->cache->get($url); - } - - function cache_set ($url, $object) { - if ( $this->wp_object_cache ) - return wp_cache_set($url, serialize($object), 'rss', 3600); - - return $this->cache->set($url, $object); + $this->normalize(); } -} -class RSSCache { - var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored - var $MAX_AGE = 43200; // when are files stale, default twelve hours - var $ERROR = ''; // accumulate error messages + function feed_start_element($p, $element, &$attrs) { + $el = $element = strtolower($element); + $attrs = array_change_key_case($attrs, CASE_LOWER); - function RSSCache ($base='', $age='') { - if ( $base ) { - $this->BASE_CACHE = $base; + // check for a namespace, and split if found + $ns = false; + if ( strpos( $element, ':' ) ) { + list($ns, $el) = split( ':', $element, 2); } - if ( $age ) { - $this->MAX_AGE = $age; + if ( $ns and $ns != 'rdf' ) { + $this->current_namespace = $ns; } - } + # if feed type isn't set, then this is first element of feed + # identify feed from root element + # + if (!isset($this->feed_type) ) { + if ( $el == 'rdf' ) { + $this->feed_type = RSS; + $this->feed_version = '1.0'; + } + elseif ( $el == 'rss' ) { + $this->feed_type = RSS; + $this->feed_version = $attrs['version']; + } + elseif ( $el == 'feed' ) { + $this->feed_type = ATOM; + $this->feed_version = $attrs['version']; + $this->inchannel = true; + } + return; + } - function set ($url, $rss) { - global $wpdb; - $cache_option = 'rss_' . $this->file_name( $url ); - $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts'; + if ( $el == 'channel' ) + { + $this->inchannel = true; + } + elseif ($el == 'item' or $el == 'entry' ) + { + $this->initem = true; + if ( isset($attrs['rdf:about']) ) { + $this->current_item['about'] = $attrs['rdf:about']; + } + } - if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") ) - add_option($cache_option, '', '', 'no'); - if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") ) - add_option($cache_timestamp, '', '', 'no'); + // if we're in the default namespace of an RSS feed, + // record textinput or image fields + elseif ( + $this->feed_type == RSS and + $this->current_namespace == '' and + $el == 'textinput' ) + { + $this->intextinput = true; + } - update_option($cache_option, $rss); - update_option($cache_timestamp, time() ); + elseif ( + $this->feed_type == RSS and + $this->current_namespace == '' and + $el == 'image' ) + { + $this->inimage = true; + } - return $cache_option; - } + # handle atom content constructs + elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) ) + { + // avoid clashing w/ RSS mod_content + if ($el == 'content' ) { + $el = 'atom_content'; + } + + $this->incontent = $el; - function get ($url) { - $this->ERROR = ""; - $cache_option = 'rss_' . $this->file_name( $url ); - if ( ! get_option( $cache_option ) ) { - $this->debug( - "Cache doesn't contain: $url (cache option: $cache_option)" - ); - return 0; } - $rss = get_option( $cache_option ); + // if inside an Atom content construct (e.g. content or summary) field treat tags as text + elseif ($this->feed_type == ATOM and $this->incontent ) + { + // if tags are inlined, then flatten + $attrs_str = join(' ', + array_map('map_attrs', + array_keys($attrs), + array_values($attrs) ) ); - return $rss; - } + $this->append_content( "<$element $attrs_str>" ); - function check_cache ( $url ) { - $this->ERROR = ""; - $cache_option = $this->file_name( $url ); - $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts'; + array_unshift( $this->stack, $el ); + } - if ( $mtime = get_option($cache_timestamp) ) { - // find how long ago the file was added to the cache - // and whether that is longer then MAX_AGE - $age = time() - $mtime; - if ( $this->MAX_AGE > $age ) { - // object exists and is current - return 'HIT'; + // Atom support many links per containging element. + // Magpie treats link elements of type rel='alternate' + // as being equivalent to RSS's simple link element. + // + elseif ($this->feed_type == ATOM and $el == 'link' ) + { + if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' ) + { + $link_el = 'link'; } else { - // object exists but is old - return 'STALE'; + $link_el = 'link_' . $attrs['rel']; } + + $this->append($link_el, $attrs['href']); } + // set stack[0] to current element else { - // object does not exist - return 'MISS'; + array_unshift($this->stack, $el); } } - function serialize ( $rss ) { - return serialize( $rss ); - } - function unserialize ( $data ) { - return unserialize( $data ); - } - function file_name ($url) { - return md5( $url ); - } + function feed_cdata ($p, $text) { - function error ($errormsg, $lvl=E_USER_WARNING) { - // append PHP's error message if track_errors enabled - if ( isset($php_errormsg) ) { - $errormsg .= " ($php_errormsg)"; - } - $this->ERROR = $errormsg; - if ( MAGPIE_DEBUG ) { - trigger_error( $errormsg, $lvl); + if ($this->feed_type == ATOM and $this->incontent) + { + $this->append_content( $text ); } else { - error_log( $errormsg, 0); - } - } - function debug ($debugmsg, $lvl=E_USER_NOTICE) { - if ( MAGPIE_DEBUG ) { - $this->error("MagpieRSS [debug] $debugmsg", $lvl); + $current_el = join('_', array_reverse($this->stack)); + $this->append($current_el, $text); } } -} - -class WP_Feed { - var $status; - var $raw_xml; - var $last_updated; - var $tree; - var $items; - var $children; - var $parser; - var $feed_type; - var $feed_version; - var $stack = array(); + function feed_end_element ($p, $el) { + $el = strtolower($el); - function WP_Feed ($source) - { - # if PHP xml isn't compiled in, die - # - if (!function_exists('xml_parser_create')) { - $this->error( "Failed to load PHP's XML Extension. " . - "http://www.php.net/manual/en/ref.xml.php", - E_USER_ERROR ); + if ( $el == 'item' or $el == 'entry' ) + { + $this->items[] = $this->current_item; + $this->current_item = array(); + $this->initem = false; } - - // Handle overloaded arg (string or HttpClient object) - if ( is_object($source) ) { - if ( $source->status >= 200 && $source->status < 300) { - $this->etag = $source->headers['etag']; - $this->last_modified = $source->headers['last-modified']; - $source = $source->content; - } else { - $this->scour(); - $this->status = $source->status; - $this->redirect_location = $source->headers->location; - $this->bathe(); - return; - } + elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' ) + { + $this->intextinput = false; } - - list($parser, $source) = $this->create_parser($source, 'UTF-8', null, true); - - if (!is_resource($parser)) { - $this->error( "Failed to create an instance of PHP's XML parser. " . - "http://www.php.net/manual/en/ref.xml.php", - E_USER_ERROR ); + elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' ) + { + $this->inimage = false; } - - $this->parser = $parser; - - # pass in parser, and a reference to this object - # setup handlers - # - xml_set_object($this->parser, $this); - xml_set_element_handler($this->parser, 'start_element', 'end_element'); - xml_set_character_data_handler( $this->parser, 'cdata'); - - $status = xml_parse( $this->parser, $source ); - - if (! $status ) { - $errorcode = xml_get_error_code( $this->parser ); - if ( $errorcode != XML_ERROR_NONE ) { - $xml_error = xml_error_string( $errorcode ); - $error_line = xml_get_current_line_number($this->parser); - $error_col = xml_get_current_column_number($this->parser); - $errormsg = "$xml_error at line $error_line, column $error_col"; - - $this->error( $errormsg ); - } + elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) ) + { + $this->incontent = false; } - - // SUPER SLOPPY FEED DISCOVERY!! TO-DO: AXE THIS CRAP!! - if ( !is_object($this->feed) || !method_exists($this->feed, 'to_xml') ) { - if ( preg_match_all('/]*href=([^ >]+)[^>]+>/i', $source, $matches) ) { - $types = array('rss', 'atom'); - foreach ( $types as $type ) - foreach ( $matches[0] as $key => $link ) - if ( preg_match('/rel=.alternate./', $link) && preg_match("/type=[^ >]*{$type}[^ >]*/", $link) ) - break 2; - $this->scour(); - $this->redirect_location = 'http://xml.wordpress.com/get/' . trim($matches[1][$key], '\'"'); - $this->status = 301; - return; - } else { - $this->scour(); - $this->status = 404; - return; - } - } else { - $this->status = 200; + elseif ($el == 'channel' or $el == 'feed' ) + { + $this->inchannel = false; } + elseif ($this->feed_type == ATOM and $this->incontent ) { + // balance tags properly + // note: i don't think this is actually neccessary + if ( $this->stack[0] == $el ) + { + $this->append_content(""); + } + else { + $this->append_content("<$el />"); + } - xml_parser_free( $this->parser ); - unset($this->parser); - - $this->bathe(); - } - - function to_xml() { - if ( is_object($this->feed) && method_exists($this->feed, 'to_xml') ) - return $this->feed->to_xml(); - - return false; - } - - // Called internally by xml_parse(). We create an object and call its start_element method. - function start_element($p, $element, &$attrs) { - $el = $element;// = strtolower($element); - // $attrs = array_change_key_case($attrs, CASE_LOWER); - - // If there is an extended class for this element, use it. - $class = 'element'; - - $maybe_class = $test_class = strtolower(str_replace(':', '_', $el)); - if ( class_exists($maybe_class) ) { - for ($classes[] = $test_class; $test_class = get_parent_class ($test_class); $classes[] = $test_class); - if ( in_array($class, $classes) ) - $class = $maybe_class; + array_shift( $this->stack ); + } + else { + array_shift( $this->stack ); } - // Instantiate an object for this element. - $object = new $class(); - - // Tell the element to start itself. - $object->start_element($p, $element, $attrs, $this); - } - - function cdata ($p, $data) { - $this->stack[0]->cdata($p, $data, $this); - } - - function end_element ($p, $el) { - $this->stack[0]->end_element($p, $el, $this); + $this->current_namespace = false; } - function create_parser($source, $out_enc, $in_enc, $detect) { - if ( substr(phpversion(),0,1) == 5) { - $parser = $this->php5_create_parser($in_enc, $detect); - } - else { - list($parser, $source) = $this->php4_create_parser($source, $in_enc, $detect); - } - if ($out_enc) { - $this->encoding = $out_enc; - xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $out_enc); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + function concat (&$str1, $str2="") { + if (!isset($str1) ) { + $str1=""; } - - return array($parser, $source); + $str1 .= $str2; } - function php5_create_parser($in_enc, $detect) { - // by default php5 does a fine job of detecting input encodings - if(!$detect && $in_enc) { - return xml_parser_create($in_enc); + function append_content($text) { + if ( $this->initem ) { + $this->concat( $this->current_item[ $this->incontent ], $text ); } - else { - return xml_parser_create(''); + elseif ( $this->inchannel ) { + $this->concat( $this->channel[ $this->incontent ], $text ); } } - /** - * Instaniate an XML parser under PHP4 - * - * Unfortunately PHP4's support for character encodings - * and especially XML and character encodings sucks. As - * long as the documents you parse only contain characters - * from the ISO-8859-1 character set (a superset of ASCII, - * and a subset of UTF-8) you're fine. However once you - * step out of that comfy little world things get mad, bad, - * and dangerous to know. - * - * The following code is based on SJM's work with FoF - * @see http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss - * - */ - function php4_create_parser($source, $in_enc, $detect) { - if ( !$detect ) { - return array(xml_parser_create($in_enc), $source); + // smart append - field and namespace aware + function append($el, $text) { + if (!$el) { + return; } - - if (!$in_enc) { - if (preg_match('//m', $source, $m)) { - $in_enc = strtoupper($m[1]); - $this->source_encoding = $in_enc; + if ( $this->current_namespace ) + { + if ( $this->initem ) { + $this->concat( + $this->current_item[ $this->current_namespace ][ $el ], $text); } - else { - $in_enc = 'UTF-8'; + elseif ($this->inchannel) { + $this->concat( + $this->channel[ $this->current_namespace][ $el ], $text ); + } + elseif ($this->intextinput) { + $this->concat( + $this->textinput[ $this->current_namespace][ $el ], $text ); + } + elseif ($this->inimage) { + $this->concat( + $this->image[ $this->current_namespace ][ $el ], $text ); } } + else { + if ( $this->initem ) { + $this->concat( + $this->current_item[ $el ], $text); + } + elseif ($this->intextinput) { + $this->concat( + $this->textinput[ $el ], $text ); + } + elseif ($this->inimage) { + $this->concat( + $this->image[ $el ], $text ); + } + elseif ($this->inchannel) { + $this->concat( + $this->channel[ $el ], $text ); + } - if ($this->known_encoding($in_enc)) { - return array(xml_parser_create($in_enc), $source); } + } - // the dectected encoding is not one of the simple encodings PHP knows - - // attempt to use the iconv extension to - // cast the XML to a known encoding - // @see http://php.net/iconv + function normalize () { + // if atom populate rss fields + if ( $this->is_atom() ) { + $this->channel['descripton'] = $this->channel['tagline']; + for ( $i = 0; $i < count($this->items); $i++) { + $item = $this->items[$i]; + if ( isset($item['summary']) ) + $item['description'] = $item['summary']; + if ( isset($item['atom_content'])) + $item['content']['encoded'] = $item['atom_content']; - if (function_exists('iconv')) { - $encoded_source = iconv($in_enc,'UTF-8', $source); - if ($encoded_source) { - return array(xml_parser_create('UTF-8'), $encoded_source); + $this->items[$i] = $item; } } + elseif ( $this->is_rss() ) { + $this->channel['tagline'] = $this->channel['description']; + for ( $i = 0; $i < count($this->items); $i++) { + $item = $this->items[$i]; + if ( isset($item['description'])) + $item['summary'] = $item['description']; + if ( isset($item['content']['encoded'] ) ) + $item['atom_content'] = $item['content']['encoded']; - // iconv didn't work, try mb_convert_encoding - // @see http://php.net/mbstring - if(function_exists('mb_convert_encoding')) { - $encoded_source = mb_convert_encoding($source, 'UTF-8', $in_enc ); - if ($encoded_source) { - return array(xml_parser_create('UTF-8'), $encoded_source); + $this->items[$i] = $item; } } + } - // else - $this->error("Feed is in an unsupported character encoding. ($in_enc) " . - "You may see strange artifacts, and mangled characters.", - E_USER_NOTICE); - - return array(xml_parser_create(), $source); + function is_rss () { + if ( $this->feed_type == RSS ) { + return $this->feed_version; + } + else { + return false; + } } - function known_encoding($enc) { - $enc = strtoupper($enc); - if ( in_array($enc, array('UTF-8', 'US-ASCII', 'ISO-8859-1')) ) { - return $enc; + function is_atom() { + if ( $this->feed_type == ATOM ) { + return $this->feed_version; } else { return false; } } - function error ($errormsg, $lvl=E_USER_WARNING) { + function map_attrs($k, $v) { + return "$k=\"$v\""; + } + + function error( $errormsg, $lvl = E_USER_WARNING ) { // append PHP's error message if track_errors enabled if ( isset($php_errormsg) ) { $errormsg .= " ($php_errormsg)"; } if ( MAGPIE_DEBUG ) { - // trigger_error( $errormsg, $lvl); - } - else { - error_log( $errormsg, 0); - } - - $notices = E_USER_NOTICE|E_NOTICE; - if ( $lvl&$notices ) { - $this->WARNING = $errormsg; + trigger_error( $errormsg, $lvl); } else { - $this->ERROR = $errormsg; + error_log( $errormsg, 0); } } - // Remove empty and |^_.*| object vars - function bathe() { - foreach ( get_object_vars($this) as $key => $data ) - if ( empty($this->$key) || substr($key, 0, 1) == '_' ) - unset($this->$key); +} +require_once( dirname(__FILE__) . '/class-snoopy.php'); + +function fetch_rss ($url) { + // initialize constants + init(); + + if ( !isset($url) ) { + // error("fetch_rss called without a url"); + return false; } - // Remove ALL object vars - function scour() { - foreach ( get_object_vars($this) as $key => $data ) - unset($this->$key); + // if cache is disabled + if ( !MAGPIE_CACHE_ON ) { + // fetch file, and parse it + $resp = _fetch_remote_file( $url ); + if ( is_success( $resp->status ) ) { + return _response_to_rss( $resp ); + } + else { + // error("Failed to fetch $url and cache is off"); + return false; + } } - - function to_magpie() { - $magpie = new stdClass(); - - foreach ( $this as $var => $value ) { - if ( $var == 'feed' ) { - continue; - } else { - $magpie->$var = $this->$var; - } + // else cache is ON + else { + // Flow + // 1. check cache + // 2. if there is a hit, make sure its fresh + // 3. if cached obj fails freshness check, fetch remote + // 4. if remote fails, return stale object, or error + + $cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE ); + + if (MAGPIE_DEBUG and $cache->ERROR) { + debug($cache->ERROR, E_USER_WARNING); } - $magpie->items = array(); - if ( is_object($this->feed) && method_exists($this->feed, 'to_magpie') ) { - $feed = $this->feed->to_magpie(); + $cache_status = 0; // response of check_cache + $request_headers = array(); // HTTP headers to send with fetch + $rss = 0; // parsed RSS object + $errormsg = 0; // errors, if any - if ( is_array($feed) ) { - foreach ( $this->feed->to_magpie() as $var => $val ) { - if ( $var == 'items' ) - $magpie->items = $val; - else - $magpie->channel["$var"] = $val; - } + if (!$cache->ERROR) { + // return cache HIT, MISS, or STALE + $cache_status = $cache->check_cache( $url ); + } + + // if object cached, and cache is fresh, return cached obj + if ( $cache_status == 'HIT' ) { + $rss = $cache->get( $url ); + if ( isset($rss) and $rss ) { + $rss->from_cache = 1; + if ( MAGPIE_DEBUG > 1) { + debug("MagpieRSS: Cache HIT", E_USER_NOTICE); + } + return $rss; } } - return $magpie; - } -} + // else attempt a conditional get + // setup headers + if ( $cache_status == 'STALE' ) { + $rss = $cache->get( $url ); + if ( $rss->etag and $rss->last_modified ) { + $request_headers['If-None-Match'] = $rss->etag; + $request_headers['If-Last-Modified'] = $rss->last_modified; + } + } -class element { - function element() { - } + $resp = _fetch_remote_file( $url, $request_headers ); - function start_element($p, $el, $attr, &$mag) { - $this->name = $el; - $this->attributes = $attr; + if (isset($resp) and $resp) { + if ($resp->status == '304' ) { + // we have the most current copy + if ( MAGPIE_DEBUG > 1) { + debug("Got 304 for $url"); + } + // reset cache on 304 (at minutillo insistent prodding) + $cache->set($url, $rss); + return $rss; + } + elseif ( is_success( $resp->status ) ) { + $rss = _response_to_rss( $resp ); + if ( $rss ) { + if (MAGPIE_DEBUG > 1) { + debug("Fetch successful"); + } + // add object to cache + $cache->set( $url, $rss ); + return $rss; + } + } + else { + $errormsg = "Failed to fetch $url. "; + if ( $resp->error ) { + # compensate for Snoopy's annoying habbit to tacking + # on '\n' + $http_error = substr($resp->error, 0, -2); + $errormsg .= "(HTTP Error: $http_error)"; + } + else { + $errormsg .= "(HTTP Response: " . $resp->response_code .')'; + } + } + } + else { + $errormsg = "Unable to retrieve RSS file for unknown reasons."; + } - array_unshift($mag->stack, $this); - } - function cdata($p, $data, &$mag) { - if ( empty($this->children) ) - $this->appendText($data); - } + // else fetch failed - function end_element($p, $el, &$mag) { - array_shift($mag->stack); + // attempt to return cached object + if ($rss) { + if ( MAGPIE_DEBUG ) { + debug("Returning STALE object for $url"); + } + return $rss; + } - $this->bathe(); + // else we totally failed + // error( $errormsg ); - if ( is_object($mag->stack[0]) ) - $mag->stack[0]->appendChild($this); - } + return false; - function wrap_cdata() { - if ( strpos($this->innerText, '<') ) { - $spacer = (substr($this->innerText, -1) == ']') ? ' ' : ''; - $this->innerText = str_replace(']]>', ']]>', $this->innerText); - $this->innerText = 'innerText . $spacer . ']]>'; - } - } + } // end if ( !MAGPIE_CACHE_ON ) { +} // end fetch_rss() - function appendText($text) { - $this->innerText .= $text; +function _fetch_remote_file ($url, $headers = "" ) { + // Snoopy is an HTTP client in PHP + $client = new Snoopy(); + $client->agent = MAGPIE_USER_AGENT; + $client->read_timeout = MAGPIE_FETCH_TIME_OUT; + $client->use_gzip = MAGPIE_USE_GZIP; + if (is_array($headers) ) { + $client->rawheaders = $headers; } - function appendChild($object) { - $this->children[] = & $object; - } + @$client->fetch($url); + return $client; - // Add self to array - function to_array(&$array) { - $self = get_object_vars($this); +} - unset($self['children']); +function _response_to_rss ($resp) { + $rss = new MagpieRSS( $resp->results ); - foreach ( $this->children as $child ) - $child->to_array($self); + // if RSS parsed successfully + if ( $rss and !$rss->ERROR) { - $array[$this->name] = $self; - } + // find Etag, and Last-Modified + foreach($resp->headers as $h) { + // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1" + if (strpos($h, ": ")) { + list($field, $val) = explode(": ", $h, 2); + } + else { + $field = $h; + $val = ""; + } - // Return self as XML - function to_xml($indent = "\t", $generation = 0) { - $self = "<$this->name"; - $self .= $this->attributes ? ' '.string_attributes($this->attributes) : ''; - if ( empty($this->innerText) && empty($this->children) ) - $self .= " />\n"; - else { - $self .= ">"; - if ( $this->children ) { - $self .= "\n"; - foreach ( $this->children as $child ) - $self .= $child->to_xml($indent, $generation + 1); - $self .= $indent ? $this->indent('', $indent, $generation) : ''; - } else { - $self .= $this->innerText; + if ( $field == 'ETag' ) { + $rss->etag = $val; + } + + if ( $field == 'Last-Modified' ) { + $rss->last_modified = $val; } - $self .= "name>\n"; } - return $indent ? $this->indent($self, $indent, $generation) : $self; - } - function indent($string, $indent, $generation) { - for ( $i = ''; strlen($i) < $generation; $i .= $indent ) ; - return $i . $string; - } + return $rss; + } // else construct error message + else { + $errormsg = "Failed to parse RSS file."; + + if ($rss) { + $errormsg .= " (" . $rss->ERROR . ")"; + } + // error($errormsg); - /** - * Gets the innerText of the first matching element in the vars - * @param string Provide any number of strings to try - * @return mixed - */ - function getChildText() { - foreach ( func_get_args() as $name ) - foreach ( $this->children as $element ) - if ( $element->name == $name ) - return $element->innerText; return false; + } // end if ($rss and !$rss->error) +} + +/*=======================================================================*\ + Function: init + Purpose: setup constants with default values + check for user overrides +\*=======================================================================*/ +function init () { + if ( defined('MAGPIE_INITALIZED') ) { + return; + } + else { + define('MAGPIE_INITALIZED', 1); } - /** - * Gets a ref to the first matching element - * @param string Provide any number of strings to try - * @return object - */ - function &getChildElement() { - foreach ( func_get_args() as $name ) - foreach ( $this->children as $key => $element ) - if ( $element->name == $name ) - return $this->children[$key]; - return false; + if ( !defined('MAGPIE_CACHE_ON') ) { + define('MAGPIE_CACHE_ON', 1); + } + + if ( !defined('MAGPIE_CACHE_DIR') ) { + define('MAGPIE_CACHE_DIR', './cache'); } - function getAttribute($name) { - if ( is_array($this->attributes) ) - foreach ( $this->attributes as $attr => $value ) - if ( $attr == $name ) - return $value; - return null; + if ( !defined('MAGPIE_CACHE_AGE') ) { + define('MAGPIE_CACHE_AGE', 60*60); // one hour } - function bathe() { - foreach ( get_object_vars($this) as $var => $value ) - if ( empty($this->$var) ) - unset($this->$var); + if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) { + define('MAGPIE_CACHE_FRESH_ONLY', 0); } - function to_magpie() { - if ( strlen(trim($this->innerText)) > 0 ) - return $this->innerText; + if ( !defined('MAGPIE_DEBUG') ) { + define('MAGPIE_DEBUG', 0); + } - if ( is_array($this->attributes) ) - $e = $this->attributes; + if ( !defined('MAGPIE_USER_AGENT') ) { + $ua = 'WordPress/' . $wp_version; - if ( is_array($this->children) && count($this->children) > 0 ) - foreach ( $this->children as $k => $c ) - $e[$k] = $this->children[$k]->to_magpie(); + if ( MAGPIE_CACHE_ON ) { + $ua = $ua . ')'; + } + else { + $ua = $ua . '; No cache)'; + } - return $e; + define('MAGPIE_USER_AGENT', $ua); } -} -// Base Atom class -class feed extends element { - function end_element($p, $el, &$mag) { - $mag->feed = $this; - $mag->is_feed = true; - $mag->last_modified = $this->last_modified(); - $mag->stack = array(); - } - function last_modified() { - $time = parse_w3cdtf($this->getChildText('modified')); - return gmdate('D, d M Y H:i:s T', $time); + if ( !defined('MAGPIE_FETCH_TIME_OUT') ) { + define('MAGPIE_FETCH_TIME_OUT', 2); // 2 second timeout } - function to_magpie() { - foreach ( $this->children as $k => $c ) { - if ( $this->children[$k]->name == 'entry' ) - $magpie['items'][] = $this->children[$k]->to_magpie(); - else - $magpie[$this->children[$k]->name] = $this->children[$k]->innerText; - } - return $magpie; + // use gzip encoding to fetch rss files if supported? + if ( !defined('MAGPIE_USE_GZIP') ) { + define('MAGPIE_USE_GZIP', true); } } -// RSS base class -class rss extends feed { - function last_modified() { - $channel =& $this->getChildElement('channel'); - $date = $channel->getChildText('pubDate', 'lastBuildDate'); - return gmdate('D, d M Y H:i:s T', strtotime($date)); - } - function to_magpie() { - return $this->children[0]->to_magpie(); - } +function is_info ($sc) { + return $sc >= 100 && $sc < 200; } -class rdf_rdf extends feed { - function to_magpie() { - $magpie = array(); - foreach ( $this->children as $k => $child ) { - if ( $this->children[$k]->name == 'item' ) - $magpie['items'][] = $this->children[$k]->to_magpie(); - elseif ( method_exists($this->children[$k], 'to_magpie') ) - $magpie[$this->children[$k]->name] = $this->children[$k]->to_magpie(); - else - $magpie[$this->children[$k]->name] = $this->children[$k]->innerText; - } - if ( is_array($magpie['channel']) ) - $magpie = array_merge($magpie['channel'], $magpie); - return $magpie; - } +function is_success ($sc) { + return $sc >= 200 && $sc < 300; } -class channel extends element { - function to_magpie() { - foreach ( $this->children as $k => $c ) { - if ( $this->children[$k]->name == 'item' ) - $magpie['items'][] = $this->children[$k]->to_magpie(); - else - $magpie[$this->children[$k]->name] = $this->children[$k]->innerText; - } - return $magpie; - } +function is_redirect ($sc) { + return $sc >= 300 && $sc < 400; } -// Atom article class -class entry extends element { - function to_magpie() { - foreach ( $this->children as $k => $v ) { - if ( is_object($this->children[$k]) ) - $value = $this->children[$k]->to_magpie(); - else - $value = $this->children[$k]->innerText; - - $name = $this->children[$k]->name; - - $norms = array( - 'dc:subject' => 'categories', - 'summary' => 'description', - ); - $name = str_replace(array_keys($norms), array_values($norms), $name); - - switch ( $name ) { - // The ones that needs to be dereferenced - case 'author': - $magpie[$name] = $value[0]; - break; - - // The ones that can be multiple - case 'categories': - $magpie[$name][] = $value; - break; - - default: - $magpie[$name] = $value; - } +function is_error ($sc) { + return $sc >= 400 && $sc < 600; +} + +function is_client_error ($sc) { + return $sc >= 400 && $sc < 500; +} + +function is_server_error ($sc) { + return $sc >= 500 && $sc < 600; +} + +class RSSCache { + var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored + var $MAX_AGE = 43200; // when are files stale, default twelve hours + var $ERROR = ''; // accumulate error messages + + function RSSCache ($base='', $age='') { + if ( $base ) { + $this->BASE_CACHE = $base; + } + if ( $age ) { + $this->MAX_AGE = $age; } - return $magpie; + } - function to_array(&$array) { - $self = get_object_vars($this); +/*=======================================================================*\ + Function: set + Purpose: add an item to the cache, keyed on url + Input: url from wich the rss file was fetched + Output: true on sucess +\*=======================================================================*/ + function set ($url, $rss) { + global $wpdb; + $cache_option = 'rss_' . $this->file_name( $url ); + $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts'; - unset($self['children']); + if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") ) + add_option($cache_option, '', '', 'no'); + if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") ) + add_option($cache_timestamp, '', '', 'no'); - foreach ( $this->children as $child ) - $child->to_array($self); + update_option($cache_option, $rss); + update_option($cache_timestamp, time() ); - $array['items'][] = $self; + return $cache_option; } -} -// RSS article class -class item extends entry { - function to_magpie() { - foreach ( $this->children as $k => $v ) { - if ( is_object($this->children[$k]) ) - $value = $this->children[$k]->to_magpie(); - else - $value = $this->children[$k]->innerText; - - $name = $this->children[$k]->name; - - $norms = array( - 'category' => 'categories', - 'content:encoded' => 'content', - 'dc:creator' => 'author', - 'wfw:commentRss' => 'comments', - 'wfw:commentRSS' => 'comments', - 'pubDate' => 'pubdate', - ); - $name = str_replace(array_keys($norms), array_values($norms), $name); - - switch ( $name ) { - // The ones that needs to be dereferenced - case 'taxo:topics': - $magpie[$name] = $value[0]; - break; - - // The ones that can be multiple - case 'categories': - $magpie[$name][] = $value; - break; - - default: - $magpie[$name] = $value; - } +/*=======================================================================*\ + Function: get + Purpose: fetch an item from the cache + Input: url from wich the rss file was fetched + Output: cached object on HIT, false on MISS +\*=======================================================================*/ + function get ($url) { + $this->ERROR = ""; + $cache_option = 'rss_' . $this->file_name( $url ); + + if ( ! get_option( $cache_option ) ) { + $this->debug( + "Cache doesn't contain: $url (cache option: $cache_option)" + ); + return 0; } - return $magpie; - } -} -class category extends element { - function to_array(&$array) { - $self = get_object_vars($this); + $rss = get_option( $cache_option ); - unset($self['children']); + return $rss; + } - foreach ( $this->children as $child ) - $child->to_array($self); +/*=======================================================================*\ + Function: check_cache + Purpose: check a url for membership in the cache + and whether the object is older then MAX_AGE (ie. STALE) + Input: url from wich the rss file was fetched + Output: cached object on HIT, false on MISS +\*=======================================================================*/ + function check_cache ( $url ) { + $this->ERROR = ""; + $cache_option = $this->file_name( $url ); + $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts'; - $array['categories'][] = $self; + if ( $mtime = get_option($cache_timestamp) ) { + // find how long ago the file was added to the cache + // and whether that is longer then MAX_AGE + $age = time() - $mtime; + if ( $this->MAX_AGE > $age ) { + // object exists and is current + return 'HIT'; + } + else { + // object exists but is old + return 'STALE'; + } + } + else { + // object does not exist + return 'MISS'; + } } -} - -class link extends element { - function end_element($p, $el, &$mag) { - if ( $href = $this->getAttribute('href') ) - $this->innerText = $href; - parent::end_element($p, $el, $mag); +/*=======================================================================*\ + Function: serialize +\*=======================================================================*/ + function serialize ( $rss ) { + return serialize( $rss ); } -} +/*=======================================================================*\ + Function: unserialize +\*=======================================================================*/ + function unserialize ( $data ) { + return unserialize( $data ); + } -/* Version 0.9, 6th April 2003 - Simon Willison ( http://simon.incutio.com/ ) - Manual: http://scripts.incutio.com/httpclient/ -*/ - -if ( !class_exists('HttpClient') ) : -class HttpClient { - // Request vars - var $host; - var $port; - var $path; - var $method; - var $postdata = ''; - var $cookies = array(); - var $referer; - var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*'; - var $accept_encoding = 'gzip'; - var $accept_language = 'en-us'; - var $user_agent = 'Incutio HttpClient v0.9'; - // Options - var $timeout = 20; - var $use_gzip = true; - var $no_cache = false; - var $persist_cookies = true; // If true, received cookies are placed in the $this->cookies array ready for the next request - // Note: This currently ignores the cookie path (and time) completely. Time is not important, - // but path could possibly lead to security problems. - var $persist_referers = true; // For each request, sends path of last request as referer - var $debug = false; - var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found - var $max_redirects = 5; - var $headers_only = false; // If true, stops receiving once headers have been read. - // Basic authorization variables - var $username; - var $password; - // Response vars - var $status; - var $headers = array(); - var $content = ''; - var $errormsg; - // Tracker variables - var $redirect_count = 0; - var $cookie_host = ''; - function HttpClient($host, $port=80) { - $this->host = $host; - $this->port = $port; - } - function get($path, $data = false) { - $this->path = $path; - $this->method = 'GET'; - if ($data) { - $this->path .= '?'.$this->buildQueryString($data); - } - return $this->doRequest(); - } - function post($path, $data) { - $this->path = $path; - $this->method = 'POST'; - $this->postdata = $this->buildQueryString($data); - return $this->doRequest(); - } - function buildQueryString($data) { - $querystring = ''; - if (is_array($data)) { - // Change data in to postable data - foreach ($data as $key => $val) { - if (is_array($val)) { - foreach ($val as $val2) { - $querystring .= urlencode($key).'='.urlencode($val2).'&'; - } - } else { - $querystring .= urlencode($key).'='.urlencode($val).'&'; - } - } - $querystring = substr($querystring, 0, -1); // Eliminate unnecessary & - } else { - $querystring = $data; - } - return $querystring; - } - function doRequest() { - // Performs the actual HTTP request, returning true or false depending on outcome - if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) { - // Set error message - switch($errno) { - case -3: - $this->errormsg = 'Socket creation failed (-3)'; - case -4: - $this->errormsg = 'DNS lookup failure (-4)'; - case -5: - $this->errormsg = 'Connection refused or timed out (-5)'; - default: - $this->errormsg = 'Connection failed ('.$errno.')'; - $this->errormsg .= ' '.$errstr; - $this->debug($this->errormsg); - } - return false; - } - socket_set_timeout($fp, $this->timeout); - $request = $this->buildRequest(); - $this->debug('Request', $request); - fwrite($fp, $request); - // Reset all the variables that should not persist between requests - $this->headers = array(); - $this->content = ''; - $this->errormsg = ''; - // Set a couple of flags - $inHeaders = true; - // Now start reading back the response - $line = fgets($fp, 4096); - // Deal with first line of returned data - if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) { - $this->errormsg = "Status code line invalid: ".htmlentities($line); - $this->debug($this->errormsg); - return false; - } - $http_version = $m[1]; // not used - $this->status = $m[2]; - $status_string = $m[3]; // not used - $this->debug(trim($line)); - while (!feof($fp)) { - $line = fgets($fp, 4096); - if ($inHeaders) { - if (trim($line) == '') { - $inHeaders = false; - $this->debug('Received Headers', $this->headers); - if ($this->headers_only) { - break; // Skip the rest of the input - } - continue; - } - if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) { - // Skip to the next header - continue; - } - $key = strtolower(trim($m[1])); - $val = trim($m[2]); - // Deal with the possibility of multiple headers of same name - if (isset($this->headers[$key])) { - if (is_array($this->headers[$key])) { - $this->headers[$key][] = $val; - } else { - $this->headers[$key] = array($this->headers[$key], $val); - } - } else { - $this->headers[$key] = $val; - } - continue; - } - // We're not in the headers, so append the line to the contents - $this->content .= $line; - } - fclose($fp); - // If data is compressed, uncompress it - if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') { - $this->debug('Content is gzip encoded, unzipping it'); - $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php - $this->content = gzinflate($this->content); - } - // If $persist_cookies, deal with any cookies - if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) { - $cookies = $this->headers['set-cookie']; - if (!is_array($cookies)) { - $cookies = array($cookies); - } - foreach ($cookies as $cookie) { - if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) { - $this->cookies[$m[1]] = $m[2]; - } - } - // Record domain of cookies for security reasons - $this->cookie_host = $this->host; - } - // If $persist_referers, set the referer ready for the next request - if ($this->persist_referers) { - $this->debug('Persisting referer: '.$this->getRequestURL()); - $this->referer = $this->getRequestURL(); - } - // Finally, if handle_redirects and a redirect is sent, do that - if ($this->handle_redirects) { - if (++$this->redirect_count >= $this->max_redirects) { - $this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')'; - $this->debug($this->errormsg); - $this->redirect_count = 0; - return false; - } - $location = isset($this->headers['location']) ? $this->headers['location'] : ''; - $uri = isset($this->headers['uri']) ? $this->headers['uri'] : ''; - if ($location || $uri) { - $url = parse_url($location.$uri); - // This will FAIL if redirect is to a different site - return $this->get($url['path']); - } - } - return true; - } - function buildRequest() { - $headers = array(); - $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding - $headers[] = "Host: {$this->host}"; - $headers[] = "User-Agent: {$this->user_agent}"; - $headers[] = "Accept: {$this->accept}"; - if ($this->use_gzip) { - $headers[] = "Accept-encoding: {$this->accept_encoding}"; - } - $headers[] = "Accept-language: {$this->accept_language}"; - if ($this->referer) { - $headers[] = "Referer: {$this->referer}"; - } - if ($this->no_cache) { - $headers[] = "Pragma: no-cache"; - $headers[] = "Cache-control: no-cache"; - } - // Cookies - if ($this->cookies) { - $cookie = 'Cookie: '; - foreach ($this->cookies as $key => $value) { - $cookie .= "$key=$value; "; - } - $headers[] = $cookie; - } - // Basic authentication - if ($this->username && $this->password) { - $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password); - } - // If this is a POST, set the content type and length - if ($this->postdata) { - $headers[] = 'Content-Type: application/x-www-form-urlencoded'; - $headers[] = 'Content-Length: '.strlen($this->postdata); - } - $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata; - return $request; - } - function getStatus() { - return $this->status; - } - function getContent() { - return $this->content; - } - function getHeaders() { - return $this->headers; - } - function getHeader($header) { - $header = strtolower($header); - if (isset($this->headers[$header])) { - return $this->headers[$header]; - } else { - return false; - } - } - function getError() { - return $this->errormsg; - } - function getCookies() { - return $this->cookies; - } - function getRequestURL() { - $url = 'http://'.$this->host; - if ($this->port != 80) { - $url .= ':'.$this->port; - } - $url .= $this->path; - return $url; - } - // Setter methods - function setUserAgent($string) { - $this->user_agent = $string; - } - function setAuthorization($username, $password) { - $this->username = $username; - $this->password = $password; - } - function setCookies($array) { - $this->cookies = $array; - } - // Option setting methods - function useGzip($boolean) { - $this->use_gzip = $boolean; - } - function setPersistCookies($boolean) { - $this->persist_cookies = $boolean; - } - function setPersistReferers($boolean) { - $this->persist_referers = $boolean; - } - function setHandleRedirects($boolean) { - $this->handle_redirects = $boolean; - } - function setMaxRedirects($num) { - $this->max_redirects = $num; - } - function setHeadersOnly($boolean) { - $this->headers_only = $boolean; - } - function setDebug($boolean) { - $this->debug = $boolean; - } - // "Quick" static methods - function quickGet($url) { - $bits = parse_url($url); - $host = $bits['host']; - $port = isset($bits['port']) ? $bits['port'] : 80; - $path = isset($bits['path']) ? $bits['path'] : '/'; - if (isset($bits['query'])) { - $path .= '?'.$bits['query']; - } - $client = new HttpClient($host, $port); - if (!$client->get($path)) { - return false; - } else { - return $client->getContent(); - } - } - function quickPost($url, $data) { - $bits = parse_url($url); - $host = $bits['host']; - $port = isset($bits['port']) ? $bits['port'] : 80; - $path = isset($bits['path']) ? $bits['path'] : '/'; - $client = new HttpClient($host, $port); - if (!$client->post($path, $data)) { - return false; - } else { - return $client->getContent(); - } - } - function debug($msg, $object = false) { - if ($this->debug) { - print '
HttpClient Debug: '.$msg; - if ($object) { - ob_start(); - print_r($object); - $content = htmlentities(ob_get_contents()); - ob_end_clean(); - print '
'.$content.'
'; - } - print '
'; - } - } -} -endif; +/*=======================================================================*\ + Function: file_name + Purpose: map url to location in cache + Input: url from wich the rss file was fetched + Output: a file name +\*=======================================================================*/ + function file_name ($url) { + return md5( $url ); + } -function string_attributes($attrs) { - return join(' ', array_map(create_function('$k,$v', 'return "$k=\"".htmlspecialchars($v)."\"";'), array_keys($attrs), array_values($attrs) ) ); +/*=======================================================================*\ + Function: error + Purpose: register error +\*=======================================================================*/ + function error ($errormsg, $lvl=E_USER_WARNING) { + // append PHP's error message if track_errors enabled + if ( isset($php_errormsg) ) { + $errormsg .= " ($php_errormsg)"; + } + $this->ERROR = $errormsg; + if ( MAGPIE_DEBUG ) { + trigger_error( $errormsg, $lvl); + } + else { + error_log( $errormsg, 0); + } + } + function debug ($debugmsg, $lvl=E_USER_NOTICE) { + if ( MAGPIE_DEBUG ) { + $this->error("MagpieRSS [debug] $debugmsg", $lvl); + } + } } -if ( !function_exists('parse_w3cdtf') ) : function parse_w3cdtf ( $date_str ) { + # regex to match wc3dtf $pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/"; if ( preg_match( $pat, $date_str, $match ) ) { list( $year, $month, $day, $hours, $minutes, $seconds) = - array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]); + array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[6]); # calc epoch for current date assuming GMT $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year); @@ -1164,7 +793,7 @@ function parse_w3cdtf ( $date_str ) { } else { list( $tz_mod, $tz_hour, $tz_min ) = - array( $match[8], $match[9], $match[10]); + array( $match[8], $match[9], $match[10]); # zero out the variables if ( ! $tz_hour ) { $tz_hour = 0; } @@ -1186,7 +815,40 @@ function parse_w3cdtf ( $date_str ) { else { return -1; } + } +function wp_rss ($url, $num_items) { + //ini_set("display_errors", false); uncomment to suppress php errors thrown if the feed is not returned. + $rss = fetch_rss($url); + if ( $rss ) { + echo ""; + } + else { + echo "an error has occured the feed is probably down, try again later."; + } } -endif; +function get_rss ($url, $num_items = 5) { // Like get posts, but for RSS + $rss = fetch_rss($url); + if ( $rss ) { + $rss->items = array_slice($rss->items, 0, $num_items); + foreach ($rss->items as $item ) { + echo "
  • \n"; + echo ""; + echo htmlentities($item['title']); + echo "
    \n"; + echo "
  • \n"; + } + } else { + return false; + } +} ?> -- cgit