最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Plugin upgrading: Widget settings

programmeradmin0浏览0评论

I have tried doing some research on this but haven't found anything solid yet. I have a plugin I am working on and between the last version and the new version we made some updates to the widget that changes some of the settings names (on the backend) and I am having trouble creating an upgrade routine to do this.

What I have done so far that seems to (mostly) work is this:

$widget = get_option( 'widget_name' );

if( is_array( $widget ) && ! empty( $widget ) ) {
    foreach( $widget as $a => $b ) {
        if( ! is_array( $b ) ) {
            continue;
        } 

        foreach( $b as $k => $v ) {
            $widget[$a]['setting1'] = $widget[$a]['oldsetting1'];
            $widget[$a]['setting2'] = $widget[$a]['oldsetting2'];
        }
    }

    update_option( 'widget_name', $widget );
}

In most of my tests this works out ok, but the problem becomes that the old widget no longer displays it's output. Only the title of the widget will show. I can fix this by going and saving each individual widget and then it will work fine, but I don't want to make my users do that.

I thought something like this might work:

$settings = $widgets->get_settings();

foreach( $settings as $s ) {

    $s['setting1'] = $s['oldsetting1'];
    $s['setting2'] = $s['oldsetting2'];

    $widgets->save_settings( $s );

}

But it seems that the save_settings() call must be wrong because this removes the widget entirely.

I am having trouble finding any sort of standard for something like this and would just like to hear any thoughs, ideas, or links you might have to doing something like this.

Thanks in advance for any help.

EDIT:

This is not actually a question about tracking license keys or upgrading plugins that aren't hosted on the WP repo. What this is more about is updating settings between 2 version of a plugin when a user upgrades.

Example:

version 1.0.0 has a setting field name

Well in version 1.1.0 we decide we need both first and last name so we change the old setting to be first_name and then add a new setting last_name.

Transferring these options if saved as post meta for a custom post type is no problem:

$old_name = get_post_meta( $post->ID, 'name', true );
$first_name = update_post_meta ( $post->ID, 'first_name', true );
delete_post_meta( $post->ID, 'name' );

So that part is easy. What I am having trouble with that seems to not be easy is doing this same thing but for WIDGET settings.

Hopefully this will clear up any confusion and help to make this be easier to answer.

EDIT 2:

Result of echo '<pre>' . print_r( $widget, true ) . '</pre>'; from first code chunk above:

Array
(
[2] => Array
    (
        [title] => Class Schedule
        [id] => 23
        [display_type] => grid
        [order] => asc
        [display_title_text] => Events on
        [paging] => 1
        [list_max_num] => 7
        [list_max_length] => days
        [list_start_offset_num] => 0
        [list_start_offset_direction] => back
        [gce_per_page_num] => 7
        [gce_events_per_page] => days
    )

[3] => Array
    (
        [title] => Examples
        [id] => 24
        [display_type] => grid
        [order] => asc
        [display_title_text] => Events on
        [paging] => 1
        [list_max_num] => 7
        [list_max_length] => days
        [list_start_offset_num] => 0
        [list_start_offset_direction] => back
        [gce_per_page_num] => 7
        [gce_events_per_page] => days
    )

[_multiwidget] => 1
)

I have tried doing some research on this but haven't found anything solid yet. I have a plugin I am working on and between the last version and the new version we made some updates to the widget that changes some of the settings names (on the backend) and I am having trouble creating an upgrade routine to do this.

What I have done so far that seems to (mostly) work is this:

$widget = get_option( 'widget_name' );

if( is_array( $widget ) && ! empty( $widget ) ) {
    foreach( $widget as $a => $b ) {
        if( ! is_array( $b ) ) {
            continue;
        } 

        foreach( $b as $k => $v ) {
            $widget[$a]['setting1'] = $widget[$a]['oldsetting1'];
            $widget[$a]['setting2'] = $widget[$a]['oldsetting2'];
        }
    }

    update_option( 'widget_name', $widget );
}

In most of my tests this works out ok, but the problem becomes that the old widget no longer displays it's output. Only the title of the widget will show. I can fix this by going and saving each individual widget and then it will work fine, but I don't want to make my users do that.

I thought something like this might work:

$settings = $widgets->get_settings();

foreach( $settings as $s ) {

    $s['setting1'] = $s['oldsetting1'];
    $s['setting2'] = $s['oldsetting2'];

    $widgets->save_settings( $s );

}

But it seems that the save_settings() call must be wrong because this removes the widget entirely.

I am having trouble finding any sort of standard for something like this and would just like to hear any thoughs, ideas, or links you might have to doing something like this.

Thanks in advance for any help.

EDIT:

This is not actually a question about tracking license keys or upgrading plugins that aren't hosted on the WP repo. What this is more about is updating settings between 2 version of a plugin when a user upgrades.

Example:

version 1.0.0 has a setting field name

Well in version 1.1.0 we decide we need both first and last name so we change the old setting to be first_name and then add a new setting last_name.

Transferring these options if saved as post meta for a custom post type is no problem:

$old_name = get_post_meta( $post->ID, 'name', true );
$first_name = update_post_meta ( $post->ID, 'first_name', true );
delete_post_meta( $post->ID, 'name' );

So that part is easy. What I am having trouble with that seems to not be easy is doing this same thing but for WIDGET settings.

Hopefully this will clear up any confusion and help to make this be easier to answer.

EDIT 2:

Result of echo '<pre>' . print_r( $widget, true ) . '</pre>'; from first code chunk above:

Array
(
[2] => Array
    (
        [title] => Class Schedule
        [id] => 23
        [display_type] => grid
        [order] => asc
        [display_title_text] => Events on
        [paging] => 1
        [list_max_num] => 7
        [list_max_length] => days
        [list_start_offset_num] => 0
        [list_start_offset_direction] => back
        [gce_per_page_num] => 7
        [gce_events_per_page] => days
    )

[3] => Array
    (
        [title] => Examples
        [id] => 24
        [display_type] => grid
        [order] => asc
        [display_title_text] => Events on
        [paging] => 1
        [list_max_num] => 7
        [list_max_length] => days
        [list_start_offset_num] => 0
        [list_start_offset_direction] => back
        [gce_per_page_num] => 7
        [gce_events_per_page] => days
    )

[_multiwidget] => 1
)
Share Improve this question edited Feb 17, 2015 at 0:18 Nick Young asked Feb 8, 2015 at 19:55 Nick YoungNick Young 3902 silver badges15 bronze badges 4
  • I just saw this article today on Tutsplus, I haven't even read it all, but it seems to be up your ally. Create a License Controlled Theme and Plugin Update System – OnethingSimple Commented Feb 11, 2015 at 0:56
  • @OnethingSimple Thanks for the reply but that doesn't quite look like what I am going for. I will update the question to make it more clear. – Nick Young Commented Feb 11, 2015 at 1:12
  • Any chance we could get a dump of what the widget settings structure looks like that you are reading in (even if you have to change some of the values). That might help give an idea of what is going wrong. e.g. echo "<pre>" . print_r($widget, true) . "</pre>"; – Privateer Commented Feb 11, 2015 at 3:08
  • @Privateer Appended to the bottom of the OP now. – Nick Young Commented Feb 12, 2015 at 18:12
Add a comment  | 

4 Answers 4

Reset to default 3 +100

I've did a quick test on just changing the option and it seems to work.

What I did is:

  1. Wrote a widget that has just 2 fields: "Title" and "Name". Add several instances of this widget to my sidebars. Been sure that they are shown correctly in frontend.
  2. Edited the class to use 3 fields: "Title" and "First Name" (to replace "Name") and added "Last Name".
  3. Edited the function that register the widget on 'widgets_init' to call a function that update the widget options:

    add_action( 'widgets_init', 'my_example_widget_register' );
    
    function my_example_widget_register() {
    
      $widget_name = 'my_example_widget';  // <-- You will probably replace this
    
      $options = get_option("widget_{$widget_name}");
    
      // if the widget is not updated, run a function that updates it
      if ($options && ! get_option("is_{$widget_name}_updated")) {
          // use class below to update options
          $updater = new MyExampleWidgetUpdater($widget_name, $options);
          $updater->update();
      }
    
      register_widget('My_Example_Widget'); // <-- You will probably replace this
    }
    
  4. Wrote a simple class to update widget options:

    class MyExampleWidgetUpdater
    {
    
      private $name;
      private $options;
    
      public function __construct($name, $options) {
         $this->name = $name;
         $this->options = $options;
      }
    
      public function update() {
        // loop all the options
        array_walk($this->options, function(&$option, $key) {
            if (is_array($option) && is_numeric($key)) {
              $option = $this->getOption($option);
            }
        });
        // update all options in DB
        update_option("widget_{$this->name}", $this->options);
        // set the widget as updated
        update_option("is_{$this->name}_updated", 1);
      }
    
      private function getOption($options) {
        if (!isset($options['name'])) {
           return $options;
        }
        $options['first_name'] = $options['name'];
        $options['last_name'] = '';
        unset($options['name']);
        return $options;
      }
    }
    
  5. I edited the widget class to save the option "is_{$widget_name}_updated" inside the update() method, in this way the updater class will never be called for new users that never installed old widget

    class My_Example_Widget {
    
        ...
    
        public function update($new_instance, $old_instance) {
            ...
    
            $widget_name = 'my_example_widget';
            update_option("is_{$widget_name}_updated", 1);
        }
    }
    
  6. I visited my site and the widgets saved with old options are displayed with no issue using new options. (Of course "last name" is always empty).

A good idea may be replace the "is_{$widget_name}_updated" option, with an option that store the actual version of the widget, in this way it will be handy next time you need an update.

Just to weigh in from a different angle - rather than auto-upgrade all settings on plugin update, simply check for an "old" setting & map to "new" settings on-the-fly:

function widget( $args, $instance ) {
    if ( isset( $instance['old_setting'] ) )
         $instance = self::_version_compat( $instance );
}

static function _version_compat( $instance ) {
    $instance['new_setting'] = $instance['old_setting'];
    // etc.

    return $instance;
}

I had the same problem with a plugin (how to update the widget options in old instances when upgrading), and I have solved as follows:


STEP 1: CHECKING PLUGIN UPGRADES

You can use the upgrader_process_complete hook, but it only fires in automatic WordPress updates, not if you manually upload the plugin. You can see an example of this hook in the code reference at developer.wordpress.

So, I have done it with a version control: 1.- defining a constant of the new/current version, and 2.- Saving this value in wp_options once we have made the updating process.

Example: if ( $saved_version != CURRENT_VERSION ) $needs_update = true;

Also you can use the version_compare() php function.


STEP 2: UPDATING OPTIONS

Create a new method in the WP_Widget class and call it from the constructor, for example $this->mywidget_check_version().

This method checks if there is a version change (as explained in step 1), and can access all the widget instances with $this->get_settings().

With a foreach() you only need to read each instance and update the keys and values you want. You only need to maintain the instance id. For example you can create a new array to add and remove new/old keys and values:

$new_instances[$id][$key] = isset( $old_instances[$id][$key] ? $old_instances[$id][$key] : $default_value[$key];

Once done, update with $this->save_settings($new_instances) and update the version key in wp_options.

Use these WP_Widget methods instead of get_option()/update_option(), to ensure integrity and compatibility. WP_Widget defines the wp_options key, the instances ids and also adds the key $settings['_multiwidget'] = 1.

Regards.

Fernando.

Off the top of my head, each instance of a widget is given some sort of unique ID. I want to say that becomes a prexfix to the keys for the widget.

I remember poking at this some time ago but can't remember what the exacts were, sorry.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论