• Resolved David Vongries

    (@davidvongries)


    Hey there,

    I want to use multiple multicheck fields on my options page like this:

    $cmb->add_field( array(
    	'name' => __( 'Field one', 'wpbf' ),
    	'id' => $prefix . 'field_one',
    	'type' => 'multicheck_inline',
    	'select_all_button' => false,
    	'options' => array(
    		'page' => 'page',
    		)
    ));
    
    $cmb->add_field( array(
    	'name' => __( 'Field two', 'wpbf' ),
    	'id' => $prefix . 'field_two',
    	'type' => 'multicheck_inline',
    	'select_all_button' => false,
    	'options' => array(
    		'page' => 'page',
    		)
    ));

    while checking both settings and hit the save button, everything seems to work fine. but as soon as I uncheck the second one, hit save and then try to uncheck the first one and hit save, the first one doesn’t save any more. it’s still checked.

    Now, what’s weird about this is that it only happens if there’s no other field following. If there’s another field (e.g. a text field) with a default value, everything works as expected.

    I also recreated the issue on one of my our plugins. Figured that this only occurs when there are only checkbox-fields on the options page and nothing else.

Viewing 4 replies - 1 through 4 (of 4 total)
  • Thread Starter David Vongries

    (@davidvongries)

    For now I just added a text field with a default value and set it to display:none. That way everything works fine.

    Plugin Contributor Michael Beckwith

    (@tw2113)

    The BenchPresser

    Can you provide your entire current config, for debugging/testing purposes. It allows us to recreate what you have exactly.

    Thread Starter David Vongries

    (@davidvongries)

    Of course, here’s the working example.

    This creates a settings page under appearance. To recreate the issue, check both checkboxes and hit save. then uncheck the second one and hit save again. then try to uncheck the first one and hit save. The first one will remain checked after the page reloads.

    <?php
    /**
     * CMB2 Theme Options
     * @version 0.1.0
     */
    class my_theme_options {
    
    	/**
     	 * Option key, and option page slug
     	 * @var string
     	 */
    	protected $key = 'my_settings';
    
    	/**
     	 * Options page metabox id
     	 * @var string
     	 */
    	protected $metabox_id = 'my_option_metabox';
    
    	/**
    	 * Options Page title
    	 * @var string
    	 */
    	protected $title = '';
    
    	/**
    	 * Options Page hook
    	 * @var string
    	 */
    	protected $options_page = '';
    
    	/**
    	 * Holds an instance of the object
    	 *
    	 * @var my_theme_options
    	 */
    	protected static $instance = null;
    
    	/**
    	 * Returns the running object
    	 *
    	 * @return my_theme_options
    	 */
    	public static function get_instance() {
    		if ( null === self::$instance ) {
    			self::$instance = new self();
    			self::$instance->hooks();
    		}
    
    		return self::$instance;
    	}
    
    	/**
    	 * Constructor
    	 * @since 0.1.0
    	 */
    	protected function __construct() {
    		// Set our title
    		$this->title = __( 'My Settings', 'textdomain' );
    	}
    
    	/**
    	 * Initiate our hooks
    	 * @since 0.1.0
    	 */
    	public function hooks() {
    		add_action( 'admin_init', array( $this, 'init' ) );
    		add_action( 'admin_menu', array( $this, 'add_options_page' ) );
    		add_action( 'cmb2_admin_init', array( $this, 'add_options_page_metabox' ) );
    	}
    
    	/**
    	 * Register our setting to WP
    	 * @since  0.1.0
    	 */
    	public function init() {
    		register_setting( $this->key, $this->key );
    	}
    
    	/**
    	 * Add menu options page
    	 * @since 0.1.0
    	 */
    	public function add_options_page() {
    		$this->options_page = add_theme_page( $this->title, $this->title, 'manage_options', 'my-settings', array( $this, 'admin_page_display' ) );
    
    		// Include CMB CSS in the head to avoid FOUC
    		add_action( "admin_print_styles-{$this->options_page}", array( 'CMB2_hookup', 'enqueue_cmb_css' ) );
    	}
    
    	/**
    	 * Admin page markup. Mostly handled by CMB2
    	 * @since  0.1.0
    	 */
    	public function admin_page_display() { ?>
    
    		<div class="wrap cmb2-options-page <?php echo $this->key; ?>">
    			<h2><?php echo esc_html( get_admin_page_title() ); ?></h2>
    
    			<?php cmb2_metabox_form( $this->metabox_id, $this->key ); ?>
    
    		</div>
    
    		<?php 
    
    	}
    
    	/**
    	 * Add the options metabox to the array of metaboxes
    	 * @since  0.1.0
    	 */
    	function add_options_page_metabox() {
    
    		$prefix = 'prefix_';
    
    		// hook in our save notices
    		add_action( "cmb2_save_options-page_fields_{$this->metabox_id}", array( $this, 'settings_notices' ), 10, 2 );
    
    		$cmb = new_cmb2_box( array(
    			'id'         => $this->metabox_id,
    			'hookup'     => false,
    			'cmb_styles' => false,
    			'show_on'    => array(
    				// These are important, don't remove
    				'key'   => 'options-page',
    				'value' => array( $this->key, )
    			),
    		) );
    
    		// Set our CMB2 fields
    		$cmb->add_field( array(
    			'name' => __( 'Global Settings', 'textdomain' ),
    			'type' => 'title',
    			'id'   => $prefix . 'global_title',
    		) );
    
    		$cmb->add_field( array(
    			'name'				=>			__( 'Setting one', 'textdomain' ),
    			'id'				=>			$prefix . 'setting_one',
    			'type'				=>			'multicheck_inline',
    			'select_all_button' =>			false,
    			'options'			=>			array(
    				'page'			=>			'page',
    				)
    		));
    
    		$cmb->add_field( array(
    			'name'				=>			__( 'Setting two', 'textdomain' ),
    			'id'				=>			$prefix . 'setting_two',
    			'type'				=>			'multicheck_inline',
    			'select_all_button' =>			false,
    			'options'			=>			array(
    				'page'			=>			'page',
    				)
    		));
    
    		// uncommenting this would fix the issue. right now I'm hiding this field via css.
    		// $cmb->add_field( array(
    		// 	'name'    => 'Test Text',
    		// 	'default' => 'I am fixing cmb2, nothing else.',
    		// 	'id' => $prefix . 'checkbox_fix',
    		// 	'type'    => 'text',
    		// ) );
    
    	}
    
    	/**
    	 * Register settings notices for display
    	 *
    	 * @since  0.1.0
    	 * @param  int   $object_id Option key
    	 * @param  array $updated   Array of updated fields
    	 * @return void
    	 */
    	public function settings_notices( $object_id, $updated ) {
    		if ( $object_id !== $this->key || empty( $updated ) ) {
    			return;
    		}
    
    		add_settings_error( $this->key . '-notices', '', __( 'Settings updated.', 'textdomain' ), 'updated' );
    		settings_errors( $this->key . '-notices' );
    	}
    
    	/**
    	 * Public getter method for retrieving protected/private variables
    	 * @since  0.1.0
    	 * @param  string  $field Field to retrieve
    	 * @return mixed          Field value or exception is thrown
    	 */
    	public function __get( $field ) {
    		// Allowed fields to retrieve
    		if ( in_array( $field, array( 'key', 'metabox_id', 'title', 'options_page' ), true ) ) {
    			return $this->{$field};
    		}
    
    		throw new Exception( 'Invalid property: ' . $field );
    	}
    
    }
    
    /**
     * Helper function to get/return the my_theme_options object
     * @since  0.1.0
     * @return my_theme_options object
     */
    function my_theme_options() {
    	return my_theme_options::get_instance();
    }
    
    /**
     * Wrapper function around cmb2_get_option
     * @since  0.1.0
     * @param  string $key     Options array key
     * @param  mixed  $default Optional default value
     * @return mixed           Option value
     */
    function prefix_get_option( $key = '', $default = array() ) {
    	if ( function_exists( 'cmb2_get_option' ) ) {
    		// Use cmb2_get_option as it passes through some key filters.
    		return cmb2_get_option( my_theme_options()->key, $key, $default );
    	}
    
    	// Fallback to get_option if CMB2 is not loaded yet.
    	$opts = get_option( my_theme_options()->key, $default );
    	$val = $default;
    	if ( 'all' == $key ) {
    		$val = $opts;
    	} elseif ( array_key_exists( $key, $opts ) && false !== $opts[ $key ] ) {
    		$val = $opts[ $key ];
    	}
    	return $val;
    }
    
    // Get it started
    my_theme_options();
    Plugin Contributor Michael Beckwith

    (@tw2113)

    The BenchPresser

    Thank you for your patience here. I meant and tried to post something previously, stating that I hadn’t forgotten, but it looks like it never succeeded.

    Anyways, all very odd. I can definitely can reproduce. It seems that when submitting, with the order described, the option isn’t fully deleted yet as it goes through a removal process for both sets of fields. Because of the delay in option removal, the original content is still present on the 2nd pass, and it doesn’t get unset at that time, causing it to get re-saved.

    I was able to get around it by setting a value in the 2nd one, and then unchecking the first ones, and that seemed to work. Then, on a 2nd save of the page, uncheck the second item and then all of them were empty finally. This was able to be done without the hidden field that you set.

    An alternate idea that may help would just be to have a “none” option that you don’t actually do anything with on the frontend or wherever you’re using these values at.

Viewing 4 replies - 1 through 4 (of 4 total)

The topic ‘multiple multichecks on settings page not saving correctly’ is closed to new replies.