Plugin options not appearing on options page using tabbed navigation
-
I’m trying to set up my plugin’s options page to use tabbed navigation via the Settings API, but the options themselves will not appear on the page. The tabs are visible, just not the settings fields.
Here is the code to create my options page:
/* ===== Admin Plugin Options ===== */ // Create submenu entry under the Settings menu function options_menu() { add_options_page( self::NAME, // Page title. This is displayed in the browser title bar. self::NAME, // Menu title. This is displayed in the Settings submenu. 'manage_options', // Capability required to access the options page for this plugin self::ID, // Menu slug array( &$this, 'options_page' ) // Function to render the options page ); } // End options_menu() // Set up options page function options_init() { /* Buffer Profiles Buffer uses profiles to store the social media accounts attached to a Buffer account. We will retrieve all social media profiles every time the plugin options page is loaded. This will only happen when the following conditions are met: - Plugin is fully authenticated with Buffer - The plugin options page is being displayed If the profiles are successfully retrieved, we can display the Buffer options. If not, we'll throw an error and no Buffer options will be shown, since we have no data from which to create them. */ if ( $this->api->is_site_authenticated() && ( isset( $_REQUEST['page'] ) && self::ID == $_REQUEST['page'] ) ) { // Get Buffer profiles for specified Buffer account $this->profile = $this->api->get_profile( $this->options['site_access_token'] ); // If WordPress returns an error, notify the user if ( is_wp_error( $this->profile ) ) { echo '<div class="error settings-error"><p><strong>Uh oh! We had a problem getting the social media accounts tied to your Buffer account. Let\'s try again.</strong><br><em>WordPress Error: ' . $this->profile->get_error_message() . '</em></p></div>'; } // If Buffer returns an error, notify the user elseif ( ! empty( $this->profile['code'] ) ) { echo '<div class="error settings-error"><p><strong>Uh oh! We had a problem getting the social media accounts tied to your Buffer account. Let\'s try again.</strong><br><em>API Error: ' . $this->profile['code'] . ' ' . $this->profile['error'] . '</em></p></div>'; } // Otherwise the profile data is valid, so we can add the Buffer options to the page else { // Set defaults for any Buffer profiles not saved in plugin options $this->buffer_profile_defaults(); // Set the array with list of enabled services $this->service = $this->services_list( $this->profile ); // Register the settings for each service foreach ( $this->service as $service ) { register_setting( self::PREFIX . $service, // The namespace for plugin options fields. This must match settings_fields() used when rendering the form. self::PREFIX . 'options', // The name of the plugin options entry in the database. array( &$this, 'options_validate' ) // The callback method to validate plugin options ); } // Call the Buffer options $this->buffer_options(); } } // Register the Buffer authentication settings register_setting( self::PREFIX . 'buffer_auth', // The namespace for plugin options fields. This must match settings_fields() used when rendering the form. self::PREFIX . 'options', // The name of the plugin options entry in the database. array( &$this, 'options_validate' ) // The callback method to validate plugin options ); // Load plugin options for Buffer authentication $this->auth_options(); // Load scripts and stylesheets for the Options page add_action( 'admin_enqueue_scripts', array( &$this, 'options_scripts' ) ); } // End options_init() /* Buffer Options */ // Generate plugin options fields from profiles function buffer_options() { // Iterate through each profile foreach ( $this->profile as $profile ) { // Add a settings section for each type of social network add_settings_section( self::PREFIX . $profile['service'], // Name of the section null, // Title of the section, unneeded here because it's handled by the tabbed navigation null, // Callback for the section - unneeded for this plugin self::ID // Page ID for the options page ); // Create a settings field to manage each social media profile add_settings_field( $profile['id'], // Field ID (use the profile ID from Buffer) '<img class="buffer_profile_avatar" src="' . $profile['avatar_https'] . '" alt="Avatar for ' . $profile['service'] . ' - ' . $profile['formatted_username'] . '"><span class="buffer_profile_username">' . $profile['formatted_username'] . '</span>', // Field title/label displayed to the user, includes avatar for profile (use the formatted username from Buffer) array( &$this, 'buffer_settings_field_callback' ), // Callback method to display the option field self::ID, // Page ID for the options page self::PREFIX . $profile['service'], // Settings section in which to display the field $profile // Send all the profile details to the callback method as an argument ); } } // End buffer_options() // Authentication options function auth_options() { // Options section add_settings_section( self::PREFIX . 'buffer_auth', // Name of the section null, // Title of the section, unneeded here because it's handled by the tabbed navigation array( &$this, 'auth_callback' ), // Callback method to display plugin options self::ID // Page ID for the options page ); // If the Client ID and Client Secret are not stored in the database, show the fields for those items if ( empty( $this->options['client_id'] ) || empty( $this->options['client_secret'] ) ) { // Buffer application client ID add_settings_field( 'client_id', // Field ID 'Client ID', // Field title/label, displayed to the user array( &$this, 'client_id_callback' ), // Callback method to display the option field self::ID, // Page ID for the options page self::PREFIX . 'buffer_auth' // Settings section in which to display the field ); // Buffer application client secret add_settings_field( 'client_secret', // Field ID 'Client secret', // Field title/label, displayed to the user array( &$this, 'client_secret_callback' ), // Callback method to display the option field self::ID, // Page ID for the options page self::PREFIX . 'buffer_auth' // Settings section in which to display the field ); } // Buffer access token to be used globally for the site (only show if Client ID and Client Secret are saved) if ( ! empty( $this->options['client_id'] ) && ! empty( $this->options['client_secret'] ) ) { // If no access token is saved in the database, display a static field label if ( empty( $this->options['site_access_token'] ) ) { // Add the settings field add_settings_field( 'site_access_token', // Field ID 'Connect to Buffer', // Field title/label, displayed to the user array( &$this, 'site_access_token_callback' ), // Callback method to display the option field self::ID, // Page ID for the options page self::PREFIX . 'buffer_auth' // Settings section in which to display the field ); } // If it is set, provide the option to disconnect from Buffer else { // Add the settings field add_settings_field( 'buffer_oauth_disconnect', // Field ID 'Disconnect from Buffer', // Field title/label, displayed to the user array( &$this, 'buffer_oauth_disconnect_callback' ), // Callback method to display the option field self::ID, // Page ID for the options page self::PREFIX . 'buffer_auth' // Settings section in which to display the field ); } } } // End auth_options() /* Plugin options callbacks */ // Callback for dynamically generated Buffer settings fields // @param array $args arguments passed to the callback from the settings field function buffer_settings_field_callback( $args ) { // If this profile is enabled in plugin options, check the box if ( ! empty( $this->options['profiles'][$args['id']]['enabled'] ) ) { $checked = 'checked'; } // If not, leave the box unchecked else { $checked = null; } // Create checkbox for enabling publishing to this service echo '<p>Enabled? <input id="' . self::PREFIX . 'options_profiles_' . $args['id'] . '_enabled" name="' . self::PREFIX . 'options[profiles][' . $args['id'] . '][enabled]" type="checkbox" ' . $checked . '></p>'; // Create text input for post message echo '<p>Message <input id="' . self::PREFIX . 'options_profiles_' . $args['id'] . '_message" name="' . self::PREFIX . 'options[profiles][' . $args['id'] . '][message]" type="text" value="' . $this->options['profiles'][$args['id']]['message'] . '" size=40></p>'; } // End buffer_settings_field_callback() /* End plugin options callbacks */ // Authorization section function auth_callback() { // If client ID & secret haven't yet been saved, display this message if ( empty( $this->options['client_id'] ) || empty( $this->options['client_secret'] ) ) { // Set the callback URL. Do not encode for a URL string. $callbackurl = $this->api->optionsurl(); // Display the message echo '<p style="color: #E30000; font-weight: bold;">In order to use this plugin, you need to <a href="https://bufferapp.com/developers/apps/create" target="_blank">register it as a Buffer application</a></p><p>It\'s easy! Once you\'ve registered the application, copy the Client ID and Client Secret from the email you receive and paste them here.</p><p><strong>Callback URL</strong>: <a href="' . $callbackurl . '">' . $callbackurl . '</a></p>'; } // If they have been saved, check whether there's an access token. If not, inform the user. else { if ( empty( $this->options['site_access_token'] ) && empty( $_REQUEST['code'] ) && empty( $_REQUEST['error'] ) ) { echo '<div class="updated settings-error"><p><strong>You\'re almost done!</strong><br>Click the button below to authenticate this site with your Buffer account.</p></div>'; } } } // End auth_callback() // Client ID function client_id_callback() { echo '<input type="text" name="' . self::PREFIX . 'options[client_id]" id="' . self::PREFIX . 'options_client_id" value="' . $this->options['client_id'] . '" size=40>'; } // End client_id_callback() // Client secret function client_secret_callback() { // If client secret is saved in the database, the field is type 'password'. If not, it's type 'text'. if ( ! empty( $this->options['client_secret'] ) ) { echo '<input type="password" name="' . self::PREFIX . 'options[client_secret]" id="' . self::PREFIX . 'options_client_secret" value="' . $this->options['client_secret'] . '" size=40>'; } else { echo '<input type="text" name="' . self::PREFIX . 'options[client_secret]" id="' . self::PREFIX . 'options_client_secret" value="' . $this->options['client_secret'] . '" size=40>'; } } // End client_id_callback() // Access token function site_access_token_callback() { // If access token is not set, run the process to retrieve it if ( empty( $this->options['site_access_token'] ) ) { // Call the OAuth method $this->api->buffer_oauth_connect(); } } // End client_id_callback() // Buffer OAuth disconnect function buffer_oauth_disconnect_callback() { // Checkbox input field echo '<input type="checkbox" name="' . self::PREFIX . 'options[oauth_disconnect]" id="' . self::PREFIX . 'options_oauth_disconnect" value="yes">'; echo '<p class="description"><strong>WARNING:</strong> checking this box will remove the account credentials for the Buffer user currently associated with this plugin.</p>'; } // Validate plugin options function options_validate( $input ) { // Set a local variable for the existing plugin options. This is so we don't mix up data. $options = $this->options; // If client ID and client secret have been changed from what's in the database, validate them if ( empty( $this->options['client_id'] ) || empty( $this->options['client_secret'] ) ) { // Check to make sure whether the provided values are hexadecimal if ( ctype_xdigit( $input['client_id'] ) && ctype_xdigit( $input['client_secret'] ) ) { $options['client_id'] = $input['client_id']; // Application client ID $options['client_secret'] = $input['client_secret']; // Application client secret } // If either one of them is not hexadecimal, throw an error else { add_settings_error ( self::ID, // Setting to which the error applies 'client-auth', // Identify the option throwing the error 'Hang on a second! The client ID or client secret you entered doesn\'t match Buffer\'s format. Double-check them both, and take another crack at it.', // Error message 'error' // The type of message it is ); } } // Access token will only be saved if Client ID and Client Secret are both already saved, but no access token is saved if ( ! empty( $this->options['client_id'] ) && ! empty( $this->options['client_secret'] ) && empty( $this->options['site_access_token'] ) ) { // Make sure a value is provided for the access token if ( ! empty( $input['site_access_token'] ) ) { // Only perform the validation tasks if the value has changed from what's in the database if ( $input['site_access_token'] != $this->options['site_access_token'] ) { // Query the plugin API to validate the access token $apiresult = $this->api->get_user( $input['site_access_token'] ); // If the API returns a user ID, and the user ID is hexadecimal, the access token is valid if ( ! empty( $apiresult['id'] ) && ctype_xdigit( $apiresult['id'] ) ) { $options['site_access_token'] = $input['site_access_token']; $options['site_user_id'] = $apiresult['id']; // Display a successful message on the next page load add_settings_error ( self::ID, // Setting to which the message applies 'site-access-token', // Identify the option throwing the message 'Hooray! Your site is now fully authenticated with Buffer, and you\'re ready to go!', // Success message 'updated' // The type of message it is ); } // If we got an error back from Buffer, notify the user elseif ( ! empty( $apiresult['code'] ) ) { add_settings_error ( self::ID, // Setting to which the error applies 'site-access-token', // Identify the option throwing the error 'Uh oh! Buffer says that something went wrong. Let\'s give it another shot!<br><em>' . $apiresult['code'] . ' ' . $apiresult['error'] . '</em>', // Error message 'error' // The type of message it is ); } // If the result was a WordPress error, show the error elseif ( is_wp_error( $apiresult ) ) { add_settings_error ( self::ID, // Setting to which the error applies 'site-access-token', // Identify the option throwing the error 'Uh oh! WordPress had an error.<br><em>' . $apiresult->get_error_message() . '</em>', // Error message 'error' // The type of message it is ); } } } // If nothing is provided for the access token, throw an error else { add_settings_error ( self::ID, // Setting to which the error applies 'site-access-token', // Identify the option throwing the error 'Whoops! It looks like you haven\'t yet authenticated with Buffer, and we can\'t continue until that\'s done. Let\'s try again!', // Error message 'error' // The type of message it is ); } } // If the site is fully authenticated, process the rest of the plugin options if ( $this->api->is_site_authenticated() ) { // If OAuth Disconnect is selected, remove the Buffer user credentials if ( ! empty( $input['oauth_disconnect'] ) ) { $options['site_access_token'] = null; $options['site_user_id'] = null; } // If OAuth Disconnect is not set, process the Buffer profile settings else { // Set local variable for 'profiles' input $profiles = $input['profiles']; // Sanitize the values of the 'enabled' checkboxes foreach ( $profiles as $id => $fields ) { // Sanitize the 'enabled' checkbox if ( ! empty( $fields['enabled'] ) ) { $profiles[$id]['enabled'] = 'on'; } else { $profile[$id]['enabled'] = null; } // Sanitize the text input for the 'message' field $profiles[$id]['message'] = sanitize_text_field( $fields['message'] ); } // Save profiles options $options['profiles'] = $profiles; } } // Return the validated options return $options; } // End options_validate() // Render options page function options_page() { // Make sure the user has the necessary privileges to manage plugin options if ( ! current_user_can( 'manage_options' ) ) { wp_die( 'Sorry, you do not have sufficient privileges to access the plugin options for ' . self::NAME . '.' ); } ?> <div class="wrap"> <h2><?php echo self::NAME; ?></h2> <?php // Check to see if 'tab' is set, and if so get the value if ( ! empty( $_GET['tab'] ) ) { $active_tab = $_GET['tab']; } // If 'tab' is not set, default to the first service in the array elseif ( empty( $_GET['tab'] ) && ! empty( $this->service ) ) { $active_tab = $this->service[0]; } // If neither 'tab' nor the service array are set, default to the Buffer Authentication tab else { $active_tab = 'buffer_auth'; } ?> <h2 class="nav-tab-wrapper"> <?php // If the service array is set, set up the tabs for the services if ( ! empty( $this->service ) ) { // Iterate through each service in the array to create each tab foreach( $this->service as $service ) { ?> <a href="?page=<?php echo self::ID; ?>&tab=<?php echo $service; ?>" class="nav-tab <?php echo $service == $active_tab ? 'nav-tab-active' : ''; ?>"><?php echo $this->apiconfig['services'][$service]['types']['profile']['name']; ?></a> <?php } } ?> <a href="?page=<?php echo self::ID; ?>&tab=buffer_auth" class="nav-tab <?php echo 'buffer_auth' == $active_tab ? 'nav-tab-active' : ''; ?>">Buffer Authentication</a> </h2><!-- .nav-tab-wrapper --> <form action="options.php" method="post"> <?php settings_fields( self::PREFIX . $active_tab ); // Retrieve the fields created for the current tab do_settings_sections( self::PREFIX . $active_tab ); // Display the section for the current tab // Show the submit button on any screen other than OAuth authorization if ( ! ( ! empty( $this->options['client_id'] ) && ! empty( $this->options['client_secret'] ) && empty( $this->options['site_access_token'] ) ) ) { submit_button(); // Form submit button generated by WordPress } ?> </form> </div> <?php } // End options_page() // Scripts and stylesheets for Options page function options_scripts() { //Load stylesheet wp_enqueue_style( self::ID, // Handle for the script plugins_url( 'admin/assets/css/options.css', $this->pluginfile ), // Location of the stylesheet array(), self::VERSION ); } /* ===== End Admin Plugin Options ===== */
Here is the same code in the Github commit where it was added, in case you’d like to see it in relation to the rest of the plugin code.
What am I missing? I should note that the options page was working without issue before I added tabbed navigation, which you can see in this commit.
Any help is appreciated, I’m sure the answer it staring me in the face but I’m just not seeing it.
Viewing 1 replies (of 1 total)
Viewing 1 replies (of 1 total)
- The topic ‘Plugin options not appearing on options page using tabbed navigation’ is closed to new replies.