Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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' );
share|improve this question
add comment

1 Answer

I am willing to be that since you have not initialized the widgets pool it cannot be duplicated.

So within your main class or plugin file you would add this:

For Class:

add_action ( 'widgets_init', array (
    &$this,
    'my_widgets' 
) );

For .php file

add_action ( 'widgets_init', 'my_widgets');

then create your function

public function my_widgets(){
    register_widget ( 'CachedFeed' );
}

Of course don't forget to include your widget class.

share|improve this answer
    
I updated my original question to reflect the response of this. First, thanks for your help. Second, I already do add_action as you can see in my updated question. The plugin saves the data I just get an undefined variable. It only happens on a new widget, not while updating or anything... I'm starting to think this is a WordPress issue... –  Jeremy Feb 23 '13 at 6:42
    
I just ignored this issue for awhile but now I realize it is due to the return false in the update. I now have it return the $old_instance instead and the undefined variable no longer happens but now an empty widget can be created... seems there is no good way to handle errors with widgets... time to sit on Google for awhile... –  Jeremy Apr 30 '13 at 14:56
    
seems like this line may be the issue $results = self::getFeed($widget_id, $feedURL, $count); you may need to check for a $widget_id before getting the feed. –  tnory56 Jun 3 '13 at 15:00
    
ok after further review it seems this line is the problem: // Fail-safe if (empty($instance['feed'])) { return false; } please look into the reason behind this.. Since you are returning false it is making the error happen. –  tnory56 Jun 3 '13 at 15:17
    
Yeah, I figured that out and now return the old instance. It seems there isn't any good way to throw errors for widgets, at least not that I found. Thanks. –  Jeremy Jun 5 '13 at 15:20
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.