I am developing a widget for cached feeds. The widget itself works exactly as I want but I noticed that if I were to delete the widget and then add a new one (or simply just add a new one) on the first save I get an undefined variable error. This is in the admin Appearence->widgets section.
Notice: Undefined index: cached_feed1450-3 in D:\Sites\test.dev\wordpress\wp-admin\includes\ajax-actions.php on line 1578
It looks to me that this may be a WordPress issue since that line is:
if ( $form = $wp_registered_widget_controls[$widget_id] )
I guess they need to check isset
I am pasting my widget here to make sure that this isn't an issue with my widget but confirmation that this is a WordPress bug. I really hope its the latter...
Thanks for you help is identifying this issue.
EDIT:
This error only happens on when saving a new widget. It only seems to happen with my widget. When editing it's not an issue.
I have a bootstrap for loading the plugin. I use this add_action( 'widgets_init', array('BootStrap1450', 'addWidgets') );
in the
/**
* Initialize the BootStrap
*/
public static function init() {
require_once(A1450COM_LIBRARY_PATH.'Functions1450.php');
// Initialize proper components (do not put into a hook!)
if (is_admin()) {
require_once(A1450COM_LIBRARY_PATH.'BackEnd1450.php');
} else {
require_once(A1450COM_LIBRARY_PATH.'FrontEnd1450.php');
FrontEnd1450::init(); // Call manually
}
// Initiation of WP hook
add_action( 'init', array('BootStrap1450', 'initWP') );
add_action( 'widgets_init', array('BootStrap1450', 'addWidgets') );
}
addWidgets is:
/**
* Adds widgets
*/
public static function addWidgets() {
include_once(A1450COM_WIDGETS_PATH.'CachedFeed1450.php');
}
The widget file then follows:
<?php
/**
* An RSS feed widget with cache controls
* @package WordPress
* @subpackage Cached Feed Widget
* @author Jeremy
*/
class CachedFeed1450 extends WP_Widget {
const TEXT_DOMAIN = 'cachedfeed';
const COUNT_LIMIT = 20;
/**
* Register widget with WordPress.
*/
public function __construct() {
$widget_options = array(
'description' => __( 'A cached feed widget that gives you custom settings per feed.', self::TEXT_DOMAIN )
);
$control_options = array(
'width' => 300
);
parent::__construct(
'cached_feed1450', // Base ID
__('Cached Feed', self::TEXT_DOMAIN), // Name
$widget_options,
$control_options
);
}
/**
* Front-end display of widget.
* *Currently only set for google news feed.
* @todo Create a feed object to handle multiple feeds different data structures
*
* @see WP_Widget::widget()
*
* @param array $args Widget arguments.
* @param array $instance Saved values from database.
*/
public function widget( $args, $instance ) {
extract( $args );
$title = apply_filters( 'widget_title', $instance['title'] );
$feedURL = $instance['feed'];
$count = absint($instance['count']);
$showWidgetTitle = $instance['show_widget_title'];
$showTitle = $instance['show_title'];
$showContent = $instance['show_content'];
$showAuthor = $instance['show_author'];
$showDate = $instance['show_date'];
$enableCache = $instance['enable_cache'];
$cacheLifetime = $instance['cache_lifetime'];
echo $before_widget;
if ( $showWidgetTitle && !empty( $title ) ) {
echo $before_title . $title . $after_title;
}
$results = self::getFeed($widget_id, $feedURL, $count);
if (!empty($results)) {
echo '<ul>';
// @todo Design a decent layout
foreach($results as $feed) {
echo '<li>';
if ($showTitle)
echo $feed->title;
if ($showContent)
echo $feed->description;
if ($showDate)
echo $feed->pubDate;
// echo $feed->link;
echo '</li>';
}
echo '</ul>';
}
echo $after_widget;
return;
}
/**
* Sanitize widget form values as they are saved.
*
* @see WP_Widget::update()
*
* @param array $new_instance Values just sent to be saved.
* @param array $old_instance Previously saved values from database.
*
* @return array Updated safe values to be saved.
*/
public function update( $new_instance, $old_instance ) {
$instance = $old_instance; // Sets defaults to old values
$instance['title'] = strip_tags($new_instance['title']);
$instance['feed'] = strip_tags($new_instance['feed']);
$instance['count'] = absint($new_instance['count']);
$instance['show_title'] = !empty($new_instance['show_title']) ? '1' : '0';
$instance['show_content'] = !empty($new_instance['show_content']) ? '1' : '0';
$instance['show_author'] = !empty($new_instance['show_author']) ? '1' : '0';
$instance['show_date'] = !empty($new_instance['show_date']) ? '1' : '0';
$instance['enable_cache'] = !empty($new_instance['enable_cache']) ? '1' : '0';
$instance['show_widget_title'] = !empty($new_instance['show_widget_title']) ? '1' : '0';
$instance['cache_lifetime'] = absint($new_instance['cache_lifetime']) * 60; // * 60 is to convert the seconds to minutes
// Clean the array
array_walk($instance, array(__CLASS__, 'trim'));
// Fail-safe
if (empty($instance['feed'])) {
return false;
}
return $instance;
}
/**
* Workaround to trim array on array_walk
* @param $value
*/
public static function trim(&$value) {
$value = trim($value);
}
/**
* Back-end widget form.
*
* @see WP_Widget::form()
*
* @param array $instance Previously saved values from database.
*/
public function form( $instance ) {
// Build data
$title = !empty($instance[ 'title' ]) ? $instance[ 'title' ] : '';
$feed = !empty($instance[ 'feed' ]) ? $instance[ 'feed' ] : '';
$count = !empty($instance[ 'count' ]) ? $instance[ 'count' ] : 0;
$cacheLifetime = !empty($instance[ 'cache_lifetime' ]) ? ($instance[ 'cache_lifetime' ] / 60) : 3600; // Default is 1 hour
$showTitle = !empty($instance[ 'show_title' ]) ? true : false;
$showContent = !empty($instance[ 'show_content' ]) ? true : false;
$showAuthor = !empty($instance[ 'show_author' ]) ? true : false;
$showDate = !empty($instance[ 'show_date' ]) ? true : false;
$enableCache = !empty($instance[ 'enable_cache' ]) ? true : false;
$showWidgetTitle = !empty($instance[ 'show_widget_title' ]) ? true : false;
?>
<p>
<label for="<?php echo $this->get_field_id( 'feed' ); ?>"><?php _e( 'Feed URL:', self::TEXT_DOMAIN ); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'feed' ); ?>" name="<?php echo $this->get_field_name( 'feed' ); ?>" type="text" value="<?php echo esc_attr( $feed ); ?>" />
</p>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title: (optional)', self::TEXT_DOMAIN ); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<p>
<input type="checkbox" id="<?php echo $this->get_field_id( 'show_widget_title' ); ?>" name="<?php echo $this->get_field_name( 'show_widget_title' ); ?>" value="1" <?php checked($showWidgetTitle, 1) ?> />
<label for="<?php echo $this->get_field_id( 'show_widget_title' ); ?>"><?php _e( 'Display the widget title?', self::TEXT_DOMAIN ); ?></label>
</p>
<p>
<label for="<?php echo $this->get_field_id( 'count' ); ?>"><?php _e( 'How many items would you like to display?:', self::TEXT_DOMAIN ); ?></label>
<select id="<?php echo $this->get_field_id( 'count' ); ?>" name="<?php echo $this->get_field_name( 'count' ); ?>" >
<?php
for($i=1; $i <= self::COUNT_LIMIT; $i++) {
echo '<option value="'.$i.'" '.selected($i, $count, false).'>'.$i.'</option>';
}
?>
</select>
</p>
<p>
<input type="checkbox" id="<?php echo $this->get_field_id( 'show_title' ); ?>" name="<?php echo $this->get_field_name( 'show_title' ); ?>" value="1" <?php checked($showTitle, 1) ?> />
<label for="<?php echo $this->get_field_id( 'show_title' ); ?>"><?php _e( 'Display the item title?', self::TEXT_DOMAIN ); ?></label>
</p>
<p>
<input type="checkbox" id="<?php echo $this->get_field_id( 'show_content' ); ?>" name="<?php echo $this->get_field_name( 'show_content' ); ?>" value="1" <?php checked($showContent, 1) ?> />
<label for="<?php echo $this->get_field_id( 'show_content' ); ?>"><?php _e( 'Display item content?', self::TEXT_DOMAIN ); ?></label>
</p>
<p>
<input type="checkbox" id="<?php echo $this->get_field_id( 'show_author' ); ?>" name="<?php echo $this->get_field_name( 'show_author' ); ?>" value="1" <?php checked($showAuthor, 1) ?> />
<label for="<?php echo $this->get_field_id( 'show_author' ); ?>"><?php _e( 'Display item author if available?', self::TEXT_DOMAIN ); ?></label>
</p>
<p>
<input type="checkbox" id="<?php echo $this->get_field_id( 'show_date' ); ?>" name="<?php echo $this->get_field_name( 'show_date' ); ?>" value="1" <?php checked($showDate, 1) ?> />
<label for="<?php echo $this->get_field_id( 'show_date' ); ?>"><?php _e( 'Display item date?', self::TEXT_DOMAIN ); ?></label>
</p>
<p>
<input type="checkbox" id="<?php echo $this->get_field_id( 'enable_cache' ); ?>" name="<?php echo $this->get_field_name( 'enable_cache' ); ?>" value="1" <?php checked($enableCache, 1) ?> class="cachedform-cache-toggle" />
<label for="<?php echo $this->get_field_id( 'enable_cache' ); ?>"><?php _e( 'Enable Cache?', self::TEXT_DOMAIN ); ?></label>
</p>
<p>
<label for="<?php echo $this->get_field_id( 'cache_lifetime' ); ?>"><?php _e( 'Cache lifetime:', self::TEXT_DOMAIN ); ?></label>
<input id="<?php echo $this->get_field_id( 'cache_lifetime' ); ?>" name="<?php echo $this->get_field_name( 'cache_lifetime' ); ?>" type="text" value="<?php echo esc_attr( $cacheLifetime ); ?>" />
<span class="description"><?php _e('In minutes', self::TEXT_DOMAIN); ?></span>
</p>
<?php
}
/**
* Get the feed data
* @param string $id
* @param string $feed_url
* @param int $limit
* @param bool $sendRaw
* @param null $cacheTime
* @return array|SimpleXmlElement
*/
private function getFeed($id, $feed_url, $limit = 0, $sendRaw = false, $cacheTime = 0) {
if (empty($cacheTime)) {
$cacheTime = 3600; // 1 hour cache by default
}
// Define cache file
$cache_file = get_theme_root() . DIRECTORY_SEPARATOR . get_template() . DIRECTORY_SEPARATOR . 'cached-feed-'.$id.'.xml';
// Check for cache
$tmpFileModifiedDate = @filemtime($cache_file);
$timedif = time() - $tmpFileModifiedDate;
if (file_exists($cache_file) && $timedif < $cacheTime) {
$string = file_get_contents($cache_file);
} else {
$string = @file_get_contents($feed_url);
// Check if an error and attempt a quick fix
if ($string === false) {
$string = @file_get_contents(urlencode($feed_url));
}
if ($string === false) {
trigger_error(__('Cached Feed Error - Fetch feed content', self::TEXT_DOMAIN));
return false;
}
if ($string) {
$f = fopen($cache_file, 'w');
if ($f === false) {
trigger_error(__('Cached Feed Error - Failed to write to cache file', self::TEXT_DOMAIN));
return false; // Failed to write to file
}
if ($f) {
fwrite($f, $string, strlen($string));
fclose($f);
}
}
}
// Return empty array if no data
if (!$string) {
return array();
}
// Get content from XML
$x = new SimpleXmlElement($string);
if ($sendRaw) {
return $x;
}
$entries = array();
$i = 0;
foreach ($x->channel->item as $entry) {
if ($limit > 0 && $i >= $limit) {
continue;
}
$entries[] = $entry;
$i++;
}
// die('<pre>'.print_r($entries, true).'</pre>');
return $entries;
}
}
register_widget( 'CachedFeed' );