0

I'm trying to update a core WordPress option, image_default_link_type, from my plugin's settings page, but WordPress is refusing to update it. The options registered by the plugin itself are updated. Here is a minimal example:

add_action( 'init', 'wpse_register_plugin_settings' );    
add_action( 'admin_menu', 'wpse_add_plugin_admin_menu_entry' );

/**
 * Register a plugin setting.
 */
function wpse_register_plugin_settings() {
    add_settings_field(
        'my_plugin_settings',
        __( 'Settings', 'my-plugin' ),
        'wpse_display_settings_field',
        'my-plugin-admin-options',
    );

    register_setting(
        'my-plugin-admin-options',
        'my_plugin_setting',
    );
}

/**
 * Callback function for the plugin settings section.
 */
function wpse_display_settings_field() {
    require_once plugin_dir_path( 'my-plugin' ) . 'partials/admin/settings/section-display.php';
}

/**
 * Add the settings page as an entry in the admin menu.
 */
function wpse_add_plugin_admin_menu_entry() {
    add_options_page(
        __( 'My Plugin Settings', 'my-plugin' ),
        __( 'My Plugin', 'my-plugin' ),
        'manage_options',
        'my-plugin-setting-page-slug',
        'wpse_output_plugin_settings_page'
    );
}

/**
 * Callback function for the plugin settings page.
 */
function wpse_output_plugin_settings_page() {
    // Check user has permissions.
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die(
            '<h1>' . esc_html__( 'You need a higher level of permission.', 'my-plugin' ) . '</h1>' .
            '<p>' . esc_html__( 'Sorry, you are not allowed to use the plugin admin settings.', 'my-plugin' ) . '</p>',
            403
        );
    }

    require_once plugin_dir_path( 'my-plugin' ) . 'partials/admin/settings/display.php';
}

Contents of section-display.php:

<label for="my_plugin_setting">
    <input type="checkbox" name="my_plugin_setting" id="my_plugin_setting" value="1" <?php checked( get_option( 'my_plugin_setting' ) ); ?> />
    <?php esc_html_e( 'Some setting', 'my-plugin' ); ?>
</label>
<br/>
<select name="image_default_link_type">
    <!-- some options -->
</select>

Contents of display.php:

<?php

/**
 * Plugin settings.
 */
?>
<div class="wrap">
    <h2><?php esc_html_e( 'My Plugin Settings', 'my-plugin' ); ?></h2>
    <div>
        <form method="post" action="options.php">
            <?php settings_fields( 'my-plugin-admin-options' ); ?>
            <?php do_settings_sections( 'my-plugin-admin-options' ); ?>
            <?php submit_button( esc_html__( 'Save Changes' ) ); ?>
        </form>
    </div>
</div>

WordPress seems to be blocking the setting from being updated. How can I enable updates to core options via my plugin's settings page?

2

The file wp-admin/options.php handles updates to options submitted via the admin area and filters the submitted option values against a list of allowed options for the option page that submitted the request.

The allowed list (called a "whitelist" before WordPress 5.5) uses the value of the variable $option_page to determine the settings that are allowed to be updated. The $option_page value will be set to whatever was specified as the argument to settings_fields() when the options page was output, which in your case is my-plugin-admin-options.

The core option image_default_link_type is only allowed to be updated when $option_page is media. That's why it doesn't get updated by your plugin's settings page. You noticed that your own plugin's settings were updated without any issue - this is because register_setting adds the option name to the allowed list for the specified $option_page automatically.

The solution is to add a filter on allowed_options (WordPress 5.5+) or whitelist_options (WordPress < 5.5) that adds the core option to the list of allowed options for the current $option_page:

add_filter( 'allowed_options', 'wpse_allow_image_default_link_type_update' );

/**
 * Allow core `image_default_link_type` setting to be updated from the
 * plugin settings page.
 *
 * @global $option_page
 */
function wpse_allow_image_default_link_type_update( $allowed_options ) {
    global $option_page;

    if ( 'my-plugin-admin-options' === $option_page ) {
        $allowed_options['my-plugin-admin-options'][] = 'image_default_link_type';
    }

    return $allowed_options;
}
1
  • 1
    Great answer Sean; I think a lot of people miss that the explanation to an answer is far more valuable to the OP and other users in the long run than just the answer itself. Kudos. – Tony Djukic Jan 5 at 3:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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