Role that can edit only widgets, not other theme options

Confirming…

Yes, there’s currently (WP 3.4.1) no way to modify the access arguments for admin menu pages. The only one, that you can modify through the public wp API is the »Comments« menu item. All others are registered by hand.

But there’s help coming from @scribu (read more at the trac ticket) who has so far taken a lot of effort to bring something more useful to core.

Explanation

When look deeper into core, then you’ll see the function wp_widgets_add_menu() inside ~/wp-includes/functions.php. This one basically does add the submenu item since WP 2.2…

function wp_widgets_add_menu() {
    global $submenu;
    $submenu["https://wordpress.stackexchange.com/questions/61016/themes.php"][7] = array( __( 'Widgets' ), 'edit_theme_options', 'widgets.php' );
    ksort( $submenu["https://wordpress.stackexchange.com/questions/61016/themes.php"], SORT_NUMERIC );
}

This function gets added to the _admin_menu action by the wp_maybe_load_widgets() function.

Intermediate work around for the menu item & widgets page

Currently the function, that loads the default widgets and registers the sub menu item (namely wp_maybe_load_widgets) is called during the plugins_loaded hook with a priority of 0.

That makes it tough to deregister it with a normal plugin. Therefore you need to use a plugin in your mu-plugins folder.

<?php
/* Plugin Name: »Kaisers« Deny Widgets page access */
! defined( 'ABSPATH' ) AND exit;

// Init the plugin
add_action( 'muplugins_loaded', array( 'wpse6106_deny_widgets', 'init' ), 0 );

class wpse6106_deny_widgets
{
    static public $instance;

    public $required_cap = 'SET_CUSTOM_CAP_HERE';

    /**
     * Get the instance of the plugin
     * @since  2012-08-07.1505
     * @return void
     */
    static function init()
    {
        null === self :: $instance AND self :: $instance = new self;
        return self :: $instance;
    }

    /**
     * Setup
     * Removes the default function that registers the widgets.php sub menu item.
     * @since  2012-08-07.1505
     * @return void
     */
    function __construct()
    {
        // remove core function...
        remove_action( 'plugins_loaded', 'wp_maybe_load_widgets', 0 );

        // ...and add our own
        add_action( 'admin_head', array( $this, 'widgets_menu_access' ), 0 );

        // Then abort any attempt to access the widgets page
        add_action( 'load-widgets.php', array( $this, 'widgets_page_access' ), 0 );
    }

    /**
     * Adds an action, that re-registers the sub menu item with a custom capability.
     * @since  2012-08-07.1505
     * @return void
     */
    function widgets_menu_access()
    {
        global $submenu;

        // Call default widgets file
        require_once( ABSPATH . WPINC . '/default-widgets.php' );

        $submenu["https://wordpress.stackexchange.com/questions/61016/themes.php"][7] = array( 
             __( 'Widgets' )
            ,$this->required_cap
            ,'widgets.php'
        );
        ksort( $submenu["https://wordpress.stackexchange.com/questions/61016/themes.php"], SORT_NUMERIC );
    }

    /**
     * Does a second check if someone without the custom cap entered the widgets page and dies.
     * @since  2012-08-07.1505
     * @return void
     */
    function widgets_page_access()
    {
        get_currentuserinfo();
        global $current_user;

        if ( ! current_user_can( $this->required_cap ) )
            wp_die( __( 'Cheatin&#8217; uh?' ) );
    }
}

Simply drop this into your MU-Plugins folder, adjust the SET_CUSTOM_CAP_HERE string inside the plugin (class variable on top ↑) and you’re ready to go. Make sure that you’re using some role manager (like Members, that allows you to give this role only to those who are meant to access the widgets page. Or add it manually with some own/custom plugin.

Also make sure, that users don’t have some left over capability stuff. If it’s not working, deactivate all plugins, switch back to TwentyTen/Eleven and do reset of your local database with a plugin like »WordPress Reset«.

Proven Result

enter image description here

Note: The plugin is tested and works in a plain vanilla installation.


Disable default widgets and the submenu items

Note: This is only for later readers, who want to get rid of it all.

If you want to completely get rid of all default widgets, then there’s a simple filter, that you can call, that stops including the ~/wp-includes/default-widgets.php file and disables the registration of the page:

add_filter( 'load_default_widgets', '__return_false' );

Leave a Comment