I'm struggling to get tabbed plugin settings, created with WordPress Settings API to work.
The first tab saves settings correctly. However, every time when I click on "Save Changes" button for any other tab, a white screen with the following error appears:
ERROR: Options page not found
Chrome and Apache log show POST 500 error. I see that form data to submit appears to be correct and according to what is submitted for the first tab that works general_section
:
option_page: account_section
action: update
_wpnonce: 2d40b0a357
_wp_http_referer: /staging/wp-admin/options-general.php?page=woo-extra-settings&tab=account
eswc_settingz[eswc_redirect]: 1
submit: Save Changes
The fields are registered only when the proper tab is selected to avoid resetting values in other tabs to defaults on save.
The code from this topic is included in my example, but it didn't fix the problem:
"Error: Options Page Not Found" on Settings Page Submission for an OOP Plugin
I used PHP console extension for Chrome and a plugin to print values to Chrome console for troubleshooting, for example:
PC::debug( 'general', 'tab name' );
This is now commented. The code can be used as a minimal part of my plugin to generate a 2-tab settings page in the default "Settings" menu and reproduce the issue that I'm having.
On tab activation, the console returns the following values (whitelist_custom_options_page
function doesn't execute):
"General" (1st tab):
tab name: general
add settings section - page: woo-extra-settings
add settings section - id: general_section
"Account" (2nd tab)
tab name: account
add settings section - page: woo-extra-settings
add settings section - id: account_section
On settings save, the values are:
"General" (1st tab):
tab name: general
add settings section - page: woo-extra-settings
add settings section - id: general_section
whitelist page: woo-extra-settings
whitelist section: general_section
whitelist option: eswc_settingz
For "Account" (2nd tab) the values are all the same, which means that on saving the settings, my code incorrectly perceives any tab as 1st tab, what is the reason for the problem that I have.
How can I fix this issue in a way that is close to my current implementation?
Here is the code:
class extra_settings_settings {
private $options;
private $settings_page_name;
private $settings_menu_name;
public function __construct() {
if ( is_admin() ) {
$this->settings_page_name = 'woo-extra-settings';
$this->settings_menu_name = 'Woo Extra Settings';
// Tracks new sections for whitelist_custom_options_page()
$this->page_sections = array();
// Must run after option_update_filter(), so priority > 10
add_action( 'whitelist_options', array( $this, 'whitelist_custom_options_page' ),11 );
// Initialize and register settings.
add_action( 'admin_init', array( $this, 'display_options' ) );
// Add settings page.
add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
}
}
public function display_options() {
$active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'general';
if ( $active_tab == 'general' ) {
//PC::debug( 'general', 'tab name:' );
register_setting( 'general_section', 'eswc_settingz', array( $this, 'sanitize_general' ) );
// ID / title / cb / page
$this->add_settings_section( 'general_section', null, array( $this, 'general_section_cb' ), $this->settings_page_name );
// ID / title / cb / page / section /args
add_settings_field( 'eswc_kill_gutenberg', __( 'Gutenberg', 'extra-settings-for-woocommerce' ), array( $this, 'eswc_kill_gutenberg_cb' ), $this->settings_page_name, 'general_section' );
add_settings_field( 'eswc_change_email_author', __( 'WP core email author', 'extra-settings-for-woocommerce' ), array( $this, 'eswc_change_email_author_cb' ), $this->settings_page_name, 'general_section' );
} else if ( $active_tab == 'account' ) {
//PC::debug( 'account', 'tab name:' );
register_setting( 'account_section', 'eswc_settingz', array( $this, 'sanitize_account' ) );
$this->add_settings_section( 'account_section', null, array( $this, 'account_section_cb' ), $this->settings_page_name );
add_settings_field( 'eswc_redirect', __( 'Login redirect', 'extra-settings-for-woocommerce' ), array( $this, 'eswc_redirect_cb' ), $this->settings_page_name, 'account_section' );
}
}
public function add_settings_page() {
// This page will be under "Settings"
$this->plugin_hook_suffix = add_options_page(
'Settings Admin', $this->settings_menu_name, 'manage_options', $this->settings_page_name, array( $this, 'create_settings_page' )
);
}
// Wrapper for wp's `add_settings_section()` that tracks custom sections
private function add_settings_section( $id, $title, $cb, $page ){
add_settings_section( $id, $title, $cb, $page );
if( $id != $page ){
if( !isset($this->page_sections[$page]))
//PC::debug( $page, 'add settings section - page:' );
//PC::debug( $id, 'add settings section - id:' );
$this->page_sections[$page] = array();
$this->page_sections[$page][$id] = $id;
}
}
// White-lists options on custom pages.
public function whitelist_custom_options_page( $whitelist_options ){
// Custom options are mapped by section id; Re-map by page slug.
foreach($this->page_sections as $page => $sections ){
//PC::debug( $page, 'whitelist page:' );
$whitelist_options[$page] = array();
foreach( $sections as $section )
//PC::debug( $section, 'whitelist section:' );
if( !empty( $whitelist_options[$section] ) )
foreach( $whitelist_options[$section] as $option )
$whitelist_options[$page][] = $option;
//PC::debug( $option, 'whitelist option:' );
}
return $whitelist_options;
}
/**
* Get the option that is saved or the default.
*
* @param string $index. The option we want to get.
*/
public function eswc_get_settings( $index = false ) {
$defaults = array ( 'eswc_kill_gutenberg' => false, 'eswc_change_email_author' => false, 'eswc_redirect' => false);
$settings = get_option( 'eswc_settingz', $defaults );
if ( $index && isset( $settings[ $index ] ) ) {
return $settings[ $index ];
}
return $settings;
}
public function create_settings_page() {
$this->options = $this->eswc_get_settings();
?>
<div class="wrap">
<h1>Extra Settings For WooCommerce</h1>
<?php
$active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'general';
?>
<h2 class="nav-tab-wrapper">
<a href="?page=woo-extra-settings&tab=general" class="nav-tab <?php echo $active_tab == 'general' ? 'nav-tab-active' : ''; ?>">General</a>
<a href="?page=woo-extra-settings&tab=account" class="nav-tab <?php echo $active_tab == 'account' ? 'nav-tab-active' : ''; ?>">Account</a>
<form method="post" action="options.php">
<?php
// Output nonce, action, and option_page fields for a settings page.
if( $active_tab == 'general' ) {
settings_fields( 'general_section' );
echo '<table class="form-table">';
do_settings_fields ($this->settings_page_name, 'general_section');
echo '</table>';
} else if ( $active_tab == 'account' ){
settings_fields( 'account_section' );
echo '<table class="form-table">';
do_settings_fields ($this->settings_page_name, 'account_section');
echo '</table>';
}
submit_button();
?>
</form>
</div>
<?php
}
/**
* Sanitize each setting field as needed
*
* @param array $input Contains all settings fields as array keys
*/
public function sanitize_general( $input ) {
$new_input = array();
if( isset( $input['eswc_kill_gutenberg'] ) )
$new_input['eswc_kill_gutenberg'] = ( $input['eswc_kill_gutenberg'] == 1 ? 1 : 0 );
if( isset( $input['eswc_change_email_author'] ) )
$new_input['eswc_change_email_author'] = ( $input['eswc_change_email_author'] == 1 ? 1 : 0 );
return $new_input;
}
public function sanitize_account( $input ) {
$new_input = array();
if( isset( $input['eswc_redirect'] ) )
$new_input['eswc_redirect'] = ( $input['eswc_redirect'] == 1 ? 1 : 0 );
return $new_input;
}
/**
* Get the settings option array and print one of its values
*/
public function eswc_kill_gutenberg_cb() {
printf(
'<fieldset>
<label><input id="eswc_kill_gutenberg" type="checkbox" name="eswc_settingz[eswc_kill_gutenberg]" value="1" %1$s />%2$s</label>
</fieldset>',
isset( $this->options['eswc_kill_gutenberg'] ) && ( 1 == $this->options['eswc_kill_gutenberg'] ) ? 'checked="checked" ':'',
__( 'Disable Gutenberg editor. Don\'t load Gutenberg-related stylesheets from WordPress core, WooCommerce and Storefront.', 'extra-settings-for-woocommerce' )
);
}
public function eswc_change_email_author_cb() {
printf(
'<fieldset>
<label><input id="eswc_change_email_author" type="checkbox" name="eswc_settingz[eswc_change_email_author]" value="1" %1$s />%2$s</label>
</fieldset>',
isset( $this->options['eswc_change_email_author'] ) && ( 1 == $this->options['eswc_change_email_author'] ) ? 'checked="checked" ':'',
__( 'Default is WordPress, [email protected]. Change to: "Site Title" from Settings > General, custom email entered below.', 'extra-settings-for-woocommerce' )
);
}
public function eswc_redirect_cb() {
printf(
'<fieldset>
<label><input id="eswc_redirect" type="checkbox" name="eswc_settingz[eswc_redirect]" value="1" %1$s />%2$s</label>
</fieldset>
<p class="description">%3$s</p>',
isset( $this->options['eswc_redirect'] ) && ( 1 == $this->options['eswc_redirect'] ) ? 'checked="checked" ':'',
__( 'After a successful login, redirect customers to previously visited page and users with admin access to dashboard.', 'extra-settings-for-woocommerce' ),
__( 'By default WooCommerce redirects to "My account" page. Customer roles: customer, subscriber. Admin access roles: administrator, shop_manager, editor, author, contributor.', 'extra-settings-for-woocommerce' )
);
}
}
$eswc_settingz_page = new extra_settings_settings();