)
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 = "
';
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||
'
)
)
),
);
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 = ' '.$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 = "
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.
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.