0

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' );

1 Answer 1

1

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.

5
  • 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
    Commented Feb 23, 2013 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
    Commented Apr 30, 2013 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. Commented Jun 3, 2013 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. Commented Jun 3, 2013 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
    Commented Jun 5, 2013 at 15:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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