Allow editors or contributors to change settings

The WordPress settings API makes it easy to add shared content to a site for administrators to edit. But it would be nice to let other roles edit some settings too.

When we register a setting in the WordPress settings API, that setting can be edited and saved only by administrators by default. This is a good thing, because often settings allow major changes to the behaviour and configuration of a website.

But sometimes we want to use the settings API to let clients easily change some common site content, like:

  • contact details — email address, phone number, Google Maps link
  • site-wide disclaimer text
  • site-wide notices

Clients often like to delegate editing content like that to staff with limited access to the website. We can allow them to edit selected settings through a WordPress filter hook:

option_page_capability_{$option_page}

If our setting is named “example_disclaimer”, we can add a filter hook to option_page_capability_example_disclaimer and change the capability required for editing that setting. The default capability is manage_options, and we can change it to edit_pages so that anyone with an Editor or Administrator role can edit that setting.

Here’s a working example for contact details in settings, without the form HTML:

const OPT_SETTINGS           = 'example_settings';

const SETTING_PHONE          = 'phone';
const SETTING_EMAIL          = 'email';
const SETTING_OFFICE_HOURS   = 'office_hours';

/**
 * register settings
 */
add_action('admin_init', function() {
    add_settings_section(OPT_SETTINGS, false, false, OPT_SETTINGS);
    register_setting(OPT_SETTINGS, OPT_SETTINGS, __NAMESPACE__ . '\\settings_validate');
});

/**
 * register admin menu items
 */
add_action('admin_menu', function() {
    add_options_page('Contact details', 'Contact details', 'edit_pages', 'example', __NAMESPACE__ . '\\settings_page');
}, 5);

/**
 * allow Editor role to save settings
 * @param string $capability
 * @return string
 */
add_filter('option_page_capability_' . OPT_SETTINGS, function($capability) {
    return 'edit_pages';
});

/**
* show settings page
*/
function settings_page() {
    $settings = get_option(OPT_SETTINGS);
    require __DIR__ . '/views/admin-settings.php';
}

/**
* validate settings on save
* @param array $input
* @return array
*/
function settings_validate($input) {
    $output = [];

    $output[SETTING_PHONE]        = trim(sanitize_text_field($input[SETTING_PHONE] ?? ''));
    $output[SETTING_EMAIL]        = trim(sanitize_text_field($input[SETTING_EMAIL] ?? ''));
    $output[SETTING_OFFICE_HOURS] = trim(sanitize_text_field($input[SETTING_OFFICE_HOURS] ?? ''));

    return $output;
}

Of course, we don’t have to add our settings page under the Settings menu; we can attach it to whichever admin menu item we’d like to. For example, I like to add the disclaimer text to the Pages menu:

/**
 * register admin menu items
 */
add_action('admin_menu', function() {
    add_pages_page('Disclaimer', 'Disclaimer', 'edit_pages', 'example-disclaimer', __NAMESPACE__ . '\\disclaimer_page');
});

So job can be done using the simple available tools (settings API) without requiring everyone to be an administrator.