) var $output_format; // how will we display each feed item? (e.g.
  • ^title$
  • ) var $output_end; // what HTML will we follow the feed items with? (e.g. ) var $utf; // convert the feed to UTF? var $icon; // URL to an RSS icon to display var $display_empty; // Display an error when feed is down/empty? (false will hide the widget when feed is down) var $reverse_order; // reverse order of feed items? var $replace_you; // replace you? // data used internally var $number; // which widget is this? You can use multiple widgets, so we need to know which we're working with here. var $md5; // md5 hash of url var $md5_option; // name of option where we cache the feed var $cached; // cached feed contents var $md5_option_ts; // name of option where we save the cache's timestamp var $flushed=false; // indicates that feed's cache was purged // rss channel info var $link; // link to origin website var $desc; // feed's description // rss item info var $tokens; // holds tokens like ^link$, ^title$, etc. var $items; // holds the items output as a string ///////////// REQUIRED METHODS // REQUIRED. Defines widget's name and such. function KB_Adv_RSS() { $widget_ops = array( 'description' => 'Entries from any RSS or Atom feed, displayed how you want them', 'classname'=>'widget_kbrss' ); $control_ops = array( 'width' => 700, 'height' => 580 ); $this->WP_Widget( 'kb-advanced-rss', 'KB Advanced RSS', $widget_ops, $control_ops ); } // REQUIRED. Displays widget output. Relies heavily on helper options defined below. function widget($args, $instance) { $i=1; /* check for errors if ( isset($instance['error']) && $instance['error'] ){ echo ''; return false; } */ // load up magpie if ( !kbar_load_magpie() ){ echo ''; return false; } // load options, set defaults if ( !$this->load_options($instance)){ echo ''; return false; } // detect output tokens, like ^title$ and such if ( !$this->detect_tokens() ){ echo ''; return false; } // check the rss cache $this->force_cache(); // load up the feed and process it if ( !$this->get_feed() ){ // fails if feed is down or empty echo ''; return false; } // display widget extract($args, EXTR_SKIP); echo $before_widget; if ( $this->title ) echo $before_title . $this->title . $after_title; echo $this->output_begin; echo $this->items; echo $this->output_end; echo $after_widget; } // REQUIRED. Processes submitted admin form. function update($new_instance, $old_instance) { $testurl = ( $new_instance['url'] != $old_instance['url'] ); return KB_Adv_RSS_process( $new_instance, $testurl ); } // REQUIRED. Displays widget's admin form. function form($instance) { /// UPDATED if ( empty($instance) ){ // set defaults $output_format = "
  • ^title\$
  • "; $output_begin = ""; if ( file_exists(dirname(__FILE__) . '/rss.png') ){ $icon = str_replace(ABSPATH, get_settings('siteurl').'/', dirname(__FILE__)) . '/rss.png'; }else{ $icon = includes_url('images/rss.png'); } $instance = array( 'title' => '', 'url' => '', 'items' => 10, 'error' => false, 'icon'=>$icon, 'linktitle'=>0, 'display_empty'=>0, 'reverse_order'=>0, 'replace_you'=>0, 'utf'=>0, 'output_format'=>$output_format, 'output_begin'=>$output_begin, 'output_end'=>$output_end ); } $instance['number'] = $this->number; $this->build_form( $instance ); } ////////////////////////////////// ////////////////////////////////// ////////////////////////////////// // Helper methods for OUTPUT ($this->widget()) ////////////////////////////////// ////////////////////////////////// ////////////////////////////////// // defines all vars used in producing feed output function load_options( $instance ){ // title to display above widget $this->title = $instance['title']; $this->linktitle = $instance['linktitle']; // number of feed items to display $this->num_items = (int) $instance['items']; if ( empty($this->num_items) || ($this->num_items < 1) || ($this->num_items > KBRSS_MAXITEMS) ) $this->num_items = KBRSS_MAXITEMS; // feed url $url = $instance['url']; // If the feed URL is given as "feed:http://example.com/rss", lop off the "feed:' part: while ( stristr($url, 'http') != $url ) $url = substr($url, 1); if ( empty($url) ) return false; $this->url = $url; // for caching $this->md5 = md5($this->url); $this->md5_option = 'rss_' . $this->md5; $this->md5_option_ts = $this->md5_option . '_ts'; // formatting options $this->output_begin = $instance['output_begin']; $this->output_format = $instance['output_format']; $this->output_end = $instance['output_end']; $this->utf = $instance['utf']; $this->display_empty = $instance['display_empty']; $this->reverse_order = $instance['reverse_order']; $this->replace_you = $instance['replace_you']; // icon? $this->icon = $instance['icon']; // default format: if ( empty($this->output_format) ) $this->output_format = '
  • ^title$
  • '; return true; } /* $this->detect_tokens() scans widget's "output format" option to figure out which "tokens" (item fields) to display, and how. returns an array of tokens, each of which is stored as an array, like this: array( array( 'slug'=> token, exactly as written in widget's options , 'field'=> name of field in rss--might be the same as 'slug', 'opts'=>array of options, or NULL if no options. ), // a basic example: array( 'slug'=>'^title$', // this is what you write in widget's options to display title 'field'=>'title', // this is the name of the field: "title" 'opts'=>null // no options (e.g. trimming) specified ), // (the following examples omit the keys to keep it brief; pretend the keys are still there) array('^description$', 'description', null), // display the item's description array('^description%%75$', 'description', array('trim'=>75)), // display the item's description, trimmed to 75 chars max array('^dc=>creator$', 'dc', array('subfield'=>'creator')), // displays the item's dc:creator field array('^dc=>creator&&5$', 'dc', array('subfield'=>'creator', 'trim'=>5)), // displays the item's dc:creator field, trimmed to 5 chars max // a complicated example: looping fields in an array. this will loop through all fields in "categories" array (this was useful on old versions of WP) array( 'slug'=>'^categories||
  • ||
  • $', 'field'=>'categories', 'opts'=>array( 'loop'=>array( 'before'=>'
  • ', 'after'=>'
  • ' ) ) ), ); done with examples. */ function detect_tokens(){ if (''==$this->output_format) return false; preg_match_all( '~\^([^$]+)\$~', $this->output_format, $matches, PREG_SET_ORDER); /* $matches will look something like this [0] => Array ( [0] => ^title$, [1] => title ) [1] => Array ( [0] => ^description%%75$, [1] => description%%75 ) */ if (!is_array($matches) || empty($matches)) return false; $tokens = array(); $used = array(); foreach( $matches as $match ){ // if they use the same token twice, let's not insert it into the tokens array twice: if ( in_array($match[0], $used) ) continue; $used[] = $match[0]; // initialize (critical) $token = array(); $token['slug'] = $match[0]; $token['opts'] = array(); // important // THE NEW SYNTAX: ^fieldname[opts:trim=50<rim=30&date=]$ if ( strpos($match[1], '[opts:') ){ $explode = explode( '[opts:', $match[1], 2 ); $match[1] = $explode[0]; $opts = substr( $explode[1], 0, -1 ); // cut off ] at the end parse_str( $opts, $options ); $token['opts'] = array_merge( $token['opts'], $options ); } // BACKWARDS COMPATIBILITY: LOOK FOR %%, =>, || // detect options: Trim? %% if ( strpos($match[1], '%%') ){ $explode = explode( '%%', $match[1] ); $match[1] = $explode[0]; $token['opts']['trim'] = $explode[1]; } // detect options: displaying arrays if ( strpos($match[1], '=>') ){ $explode = explode( '=>', $match[1], 2); $match[1] = $explode[0]; $token['opts']['subfield'] = $explode[1]; }elseif( strpos($match[1], '||') ){ $explode = explode( '||', $match[1], 3); $match[1] = $explode[0]; $token['opts']['loop'] = true; $token['opts']['beforeloop'] = $explode[1]; $token['opts']['afterloop'] = $explode[2]; } $token['field'] = $match[1]; // all done. add to master array $tokens[] = $token; } if (empty($tokens)) return false; $this->tokens = $tokens; return true; } // check whether to clear the rss cache function force_cache(){ // before purging cache, let's make sure we've got a // copy of it handy: $rss = get_option( $this->md5_option ); if (is_object( $rss )) $this->cached = $rss; // if logged in as admin, you can force a cache flush by adding ?kbrss_cache=flush to your blog URL global $userdata; if ( ('flush' == $_GET['kbrss_cache']) && ($userdata->user_level >= 7) ){ delete_option( $this->md5_option ); $this->flushed = true; return; } // Regardless, we'll flush the cache every hour. (WP should flush hourly on its own, though.) $cachetime = get_option( $this->md5_option_ts ); if ( $cachetime < ( time() - 3600 ) ){ delete_option( $this->md5_option ); $this->flushed = true; } } // fetch the feed and format it for display (requires that other methods be called first to // prepare the data; see $this->widget(). function get_feed(){ if ( !$this->flushed && $this->cached ){ $rss = $this->cached; // use our cached copy }else{ $rss = @fetch_rss($this->url); // get fresh copy if ( !is_array($rss->items) || empty( $rss->items ) ){ // if fetch failed, then use our cached copy $rss = $this->cached; // use cached copy // if we flushed the cache, then restore it since // we weren't able to update it if ( $this->flushed ) update_option( $this->md5_option, $rss ); }elseif ( !$rss->from_cache ){ // fetch didn't fail, apparently; we check // $rss->from_cache to verify that fetch wasn't from // magpie's cache. It wasn't, so we should update // our cache: update_option( $this->md5_option, $rss ); update_option( $this->md5_option_ts, time() ); } } // PART I: PREPARE CHANNEL INFORMATION: // link to RSS's origin site $this->link = esc_url(strip_tags($rss->channel['link'])); while( strstr($this->link, 'http') != $this->link ) $this->link = substr( $this->link, 1 ); // feed description $this->desc = esc_attr(strip_tags(@html_entity_decode($rss->channel['description'], ENT_QUOTES, get_option('blog_charset')))); // clean up url before displaying to screen $this->url = esc_url(strip_tags($this->url)); // link title to source URL? if ( ($this->linktitle) && $this->title ) $this->title = ''. $this->title .''; // add icon to title, if necessary if ( $this->icon ) $this->title = 'RSS '.$this->title; // PART II: PREPARE ITEM INFORMATION if ( is_array($rss->items) && !empty( $rss->items ) ){ // if there are items in the feed: if ($this->reverse_order) $rss->items = array_reverse( $rss->items ); $rss->items = array_slice($rss->items, 0, $this->num_items); // initialize output. Note that $rss->items, $this->items, and $instance['items'] are NOT similar. $this->items = ''; // holds our output while we assemble it // loop through each item in the feed foreach( $rss->items as $item ){ // loop through each token that we need to find $find = array(); // initialize foreach( $this->tokens as $token ){ $replace = ''; // initialize // how to display this field? if ( is_array($item[ $token['field'] ]) ){ // display a subfield: if ( $token['opts']['subfield'] ){ $replace = $item[ $token['field'] ][ $token['opts']['subfield'] ]; $replace = $this->item_cleanup( $replace, $token['opts'] ); // loop through items in this field: }elseif ( $token['opts']['loop'] ) { foreach( $item[ $token['field'] ] as $subfield ){ $subfield = $this->item_cleanup( $subfield, $token['opts'] ); $replace .= $token['opts']['beforeloop'] . $subfield . $token['opts']['afterloop']; } } }else{ $replace = $item[ $token['field'] ]; $is_url = ('link'==$token['slug']) ? true : false; $replace = $this->item_cleanup( $replace, $token['opts'], $is_url ); } $find[ $token['slug'] ] = $replace; } $keys = array_keys( $find ); $vals = array_values( $find ); $this->items .= str_replace( $keys, $vals, $this->output_format ); } // Admin nickname mod if ($this->replace_you) { $user_info = get_userdata(1); $admin_nickname = $user_info->nickname; $this->items = str_ireplace(array("your", "you"), array($admin_nickname . "'s", $admin_nickname, $admin_nickname), $this->items); } // Admin nickname mod if ($this->utf) $this->items = utf8_encode( $this->items ); }else{ // no feed, display an error message if ($this->display_empty){ if ( 'output_format, 0, 3 ) ) $this->items = '
  • ' . __( 'An error has occurred; the feed is probably down. Try again later.' ) . '
  • '; else $this->items = __( 'An error has occurred; the feed is probably down. Try again later.' ); }else{ // display nothing when feed is down/empty $this->items = ''; return false; } } return true; // always return true, except for one case above } // helper for the items loop in previous function. This is where we implement most of the // options. This is the part of the plugin to edit if you // want to add a new functionality. See the FAQ for details. function item_cleanup($text,$opts=false,$url=false){ // some cleanup for security. To bypass, set KBRSS_WPMU false (top of this file) and use [opts:bypasssecurity] in field options. if (KBRSS_WPMU || !is_array($opts) || !array_key_exists('bypasssecurity',$opts)){ if ($url) $text = clean_url(strip_tags($text)); else $text = str_replace(array("\n", "\r"), ' ', attribute_escape(strip_tags(html_entity_decode($text, ENT_QUOTES)))); } // apply opts, if given: if (!is_array($opts)) return $text; extract($opts, EXTR_SKIP); // date formatting on pubdate if ($date){ $text = $this->make_date($text,$date); } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// BEGIN CUSTOMIZATIONS ////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// // If you want to write customizations, put them here. The variable to modify is $text. See the FAQ. ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// END CUSTOMIZATIONS //////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// // left trimming: $ltrim = (is_numeric($ltrim) && 0<$ltrim) ? (int) $ltrim : null; if (is_int($ltrim)) $text = substr( $text, $ltrim ); // length trimming (do after left trimming) $trim = (is_numeric($trim) && 0<$trim) ? (int) $trim : null; if (is_int($trim)) $text = substr( $text, 0, $trim ); return $text; } // another helper for preceding function make_date($string, $format){ $time = strtotime( $string ); if (false===$time || -1===$time) return $string; return date( $format, $time ); } ////////////////////////////////// ////////////////////////////////// ////////////////////////////////// // Helper methods for ADMIN. ////////////////////////////////// ////////////////////////////////// ////////////////////////////////// // displays the widget's option form in the admin screen function build_form( $args, $inputs = null ) { // UPDATED $default_inputs = array( 'url' => true, 'title' => true, 'items' => true, 'icon'=>true, 'linktitle'=>true, 'display_empty'=> true, 'reverse_order'=>true, 'replace_you'=>true, 'utf'=>true, 'output_format'=>true, 'output_begin'=>true, 'output_end'=>true ); $inputs = wp_parse_args( $inputs, $default_inputs ); extract( $args ); //extract( $inputs, EXTR_SKIP); // scrub for form and set defaults where necessary $number = esc_attr( $number ); $url = esc_url( $url ); $icon = esc_url ( $icon ); $title = esc_attr( $title ); $linktitle = (int) $linktitle; $display_empty = (int) $display_empty; $reverse_order = (int) $reverse_order; $replace_you = (int) $replace_you; $utf = (int) $utf; $output_begin = esc_attr( $output_begin ); $output_end = esc_attr( $output_end ); $items = (int) $items; if ( $items < 1 || KBRSS_MAXITEMS < $items ) $items = 10; if ( '' == $output_format ) $output_format = "
  • ^title\$
  • "; $output_format = esc_attr( $output_format ); if ( !empty($error) ) echo '

    ' . sprintf( __('KB Advanced RSS Error: %s'), $error) . '

    '; // form output begins now: ?>

    For help: Read the documentation.
    /> />
    /> />
    />

     

    Formatting Options
    Use the default settings to make your feed look like it would using WP's built-in RSS widget. To customize, use the advanced fields below.

    ^element$. Default:", 'kbwidgets'); ?>
    <li><a href='^link$' title='^description$'>^title$</a></li>

    get_error_message(); } else { $link = esc_url(strip_tags($rss->get_permalink())); while ( stristr($link, 'http') != $link ) $link = substr($link, 1); } } */ return compact( 'title', 'url', 'link', 'items', 'error', 'icon', 'linktitle', 'display_empty', 'reverse_order', 'replace_you', 'utf', 'output_format', 'output_begin', 'output_end' ); } /////////////////////////////////////// /////////////////////////////////////// /////////////////////////////////////// // add a filter for troubleshooting feeds /////////////////////////////////////// /////////////////////////////////////// function widget_kbrss_troubleshooter(){ if ( !($_GET['kbrss']) ) return; global $userdata; if ( $userdata->user_level >= 7 ){ // that ought to do it // try to find magpie: if ( !kbar_load_magpie() ) wp_die( "Unable to load up magpie. Sorry." ); $rss = @fetch_rss($_GET['kbrss']); $out = "KB RSS Troubleshooter

    KB Advanced RSS Troubleshooter

    Below, you should see the feed as Magpie RSS passes it to the KB Advanced RSS widget. If you don\'t see anything, the feed might be down. Try reloading the page.

    ";
    		$out .= htmlspecialchars( print_r($rss->items, true) );
    		$out .= "
    "; print $out; die; }else{ print "

    You must be logged in as an administrator to troubleshoot feeds.

    "; die; } return; } add_action('template_redirect', 'widget_kbrss_troubleshooter'); //////////////////////////////////// // Register the widget: add_action('widgets_init', create_function('', 'return register_widget("KB_Adv_RSS");')); ?>