Question

How do I create a widget that only allows a single instance of a widget to be added through the Wordpress interface?

The default behaviour of widgets allows multiple instances to be added.

I am using the Example code from http://codex.wordpress.org/Widgets_API

I have seen a similar question where the answer was to use the old widget code, but I would like to continue to use that more modern code if possible.

I have seen plugins such as Twitter for Wordpress that give exact control over the number of instances allowed.

Thanks,
matt

Was it helpful?

Solution

I know this is a super old question, but searching for solutions 8 years later didn't yield many results aside from this unresolved question (and a few other vague answers). Nothing I found on this site worked for me so if others arrive on this page looking for a way to limit a widget-area to single-use from a specific widget, I've provided the code that worked for me below. This solution is based on an article posted by Tom McFarlin. It makes use of jQuery's sortable function to check the widgets according against criteria every time it tries to update. I made some of my own tweaks to better suit my needs.

Please note: You'll need to replace widget_area_id with your own ID for the specific WIDGET AREA as well as widget_id with your own ID for the actual WIDGET. Place this code in an admin.js file and enqueue it on the backend.

jQuery(document).ready(function($){

    $('#widget_area_id').sortable({

        update: function( evt, ui ) {

            // Check each widget in the widget area to verify that it matches the ID of the widget we want there 
            $(this).children('.widget').each(function(){
                var value = $(this).find('.widget-inside').children('form').children('.id_base').val();
                if( value !== 'widget_id' ){
                    $(this).remove();
                }
            });

            // And if there are more than one widget, remove all but one
            if ( 2 !== $(this).children().length ) {
                $(this).children(':last').remove();
            }

        }

    });

});

OTHER TIPS

Switch after the 1st instance and abort

In the following example you can find a static variable that's not set on the first instance. If the widget gets called the 1st time, we set it to true. On the 2nd run, we abort and just return without actually serving anything to the user.

class My_Widget extends WP_Widget {
    // You static instance switch
    static $instance;

    function My_Widget() {
            // We set the static class var to true on the first run
            if ( ! $this->instance ) {
                    $this->instance = true;
            } 
            // abort silently for the second instance of the widget
            else {
                    return;
            }
        // widget actual processes
    }
    function form($instance) {
        // outputs the options form on admin
    }
    function update($new_instance, $old_instance) {
        // processes widget options to be saved
    }
    function widget($args, $instance) {
        // outputs the content of the widget
    }
}
register_widget('My_Widget');

Code taken from the related Widgets API Codex article


Handling the global widgets array

Another option might be to check the global array containing all registered widgets:

function wpse32103_show_widgets()
{
    $dump  = '<pre>';
    $dump .= var_export( $GLOBALS['wp_registered_widgets'], false );
    $dump .= '</pre>';

    return print $dump;
}
add_action( 'shutdown', 'wpse32103_show_widgets' );

The output of the associative array contains the name as key, appended by -2 (nr. is incremented). You could search through the array on the init or admin_init hook and simply unset if you have found a second instance. Maybe something like the following:

function wpse32103_show_widgets()
{
        global $wp_registered_widgets;

        // Go and search for your widgets name with the above written function
        $target = 'FILL IN YOUR FOUND ARRAY KEY HERE. Without -2 (or any other appending number)';

        // Container for your targeted widget(s)
        $unsets = array();
        foreach ( array_keys( $wp_registered_widgets ) as $widget )
        {
            // remove dashes
            $widget_check = str_replace( '-', '', $widget );
            // remove numbers
            $widget_check = preg_replace( '/[^0-9]/', '', $widget );

            // if we match, do it in our container
            if ( $widget_check === $target )
                $unsets[] = $widget;
        }

        // less than one element in the container: abort
        if ( ! count ( $unsets ) > 1 )
            return;

        // preserve first element
        array_shift( array_values( $unsets ) );

        // unset all left instances from the global array
        foreach ( $unsets as $unset )
            unset ( $wp_registered_widgets[ $unset ] );

        return;
}
add_action( 'init', 'wpse32103_show_widgets' );

Edit: Doh, just saw that you didn't want to use to the old code. Oh well, I'll leave it just in case it helps someone.

WP added multi widgets in 2.8. So, I tracked down a tutorial that covered how to make a widget in WP 2.7. Here's the code:

add_action("widgets_init", array('Widget_name', 'register'));
register_activation_hook( __FILE__, array('Widget_name', 'activate'));
register_deactivation_hook( __FILE__, array('Widget_name', 'deactivate'));
class Widget_name {
  function activate(){
    $data = array( 'option1' => 'Default value' ,'option2' => 55);
    if ( ! get_option('widget_name')){
      add_option('widget_name' , $data);
    } else {
      update_option('widget_name' , $data);
    }
  }
  function deactivate(){
    delete_option('widget_name');
  }
    function control(){
        $data = get_option('widget_name');
        ?>
        <p><label>Option 1<input name="widget_name_option1"
    type="text" value="<?php echo $data['option1']; ?>" /></label></p>
        <p><label>Option 2<input name="widget_name_option2"
    type="text" value="<?php echo $data['option2']; ?>" /></label></p>
        <?php
         if (isset($_POST['widget_name_option1'])){
            $data['option1'] = attribute_escape($_POST['widget_name_option1']);
            $data['option2'] = attribute_escape($_POST['widget_name_option2']);
            update_option('widget_name', $data);
        }
    }
  function widget($args){
    echo $args['before_widget'];
    echo $args['before_title'] . 'Your widget title' . $args['after_title'];
    echo 'I am your widget';
    echo $args['after_widget'];
  }
  function register(){
        wp_register_sidebar_widget( 'my_widget_id', 'Widget name', array('Widget_name', 'widget'));
    wp_register_widget_control( 'my_widget_id', 'Widget name', array('Widget_name', 'control'));
  }
}

I replaced the calls to register_sidebar_widget and register_widget_control with wp_register_sidebar_widget and wp_register_widget_control respectively, to fix warnings about using deprecated functions. Otherwise, this is a straight copy/paste from the tutorial I linked to.

Here’s a proof of concept for a single-use widget derived from this answer:

https://github.com/glueckpress/single-use-widget

Limitation: Only works in the Customizer, not on Widget admin page.

You don't. It's too difficult to be worth it. It's how the JS editor thing works.

BUT: Create a Widget with no settings and allow setting it up on a different page. Like this:

// In your Widget use this form() method
public function form($instance){
    $settings_url = admin_url('options-general.php?page=SettingsPage');
    echo '<p>';
        echo 'To setup this <strong>Widget</strong> go to:<br />';
        echo '<a href="', $settings_url, '" target="_blank">';
            echo '<strong>Settings &raquo; Settings Page</strong>';
        echo '</a>';
    echo '</p>';
    return 'noform';
}

Use add_options_page to create the SettingsPage page for Widget management. And use get_option() to retrieve the settings for Widget Output.

Regards.

PS: If you need a full example, let me know.

Licensed under: CC-BY-SA with attribution
Not affiliated with wordpress.stackexchange
scroll top