'',
'title' => '',
// Post type slug, or 'user', 'term', 'comment', or 'options-page'.
'object_types' => array(),
/**
* The context within the screen where the boxes should display. Available contexts vary
* from screen to screen. Post edit screen contexts include 'normal', 'side', and 'advanced'.
*
* For placement in locations outside of a metabox, other options include:
* 'form_top', 'before_permalink', 'after_title', 'after_editor'
*
* Comments screen contexts include 'normal' and 'side'. Default is 'normal'.
*/
'context' => 'normal',
'priority' => 'high', // Or 10 for options pages.
'show_names' => true, // Show field names on the left.
'show_on_cb' => null, // Callback to determine if metabox should display.
'show_on' => array(), // Post IDs or page templates to display this metabox. overrides 'show_on_cb'.
'cmb_styles' => true, // Include CMB2 stylesheet.
'enqueue_js' => true, // Include CMB2 JS.
'fields' => array(),
/**
* Handles hooking CMB2 forms/metaboxes into the post/attachement/user/options-page screens
* and handles hooking in and saving those fields.
*/
'hookup' => true,
'save_fields' => true, // Will not save during hookup if false.
'closed' => false, // Default metabox to being closed.
'taxonomies' => array(),
'new_user_section' => 'add-new-user', // or 'add-existing-user'.
'new_term_section' => true,
'show_in_rest' => false,
'classes' => null, // Optionally add classes to the CMB2 wrapper.
'classes_cb' => '', // Optionally add classes to the CMB2 wrapper (via a callback).
/*
* The following parameter is for post alternate-context metaboxes only.
*
* To output the fields 'naked' (without a postbox wrapper/style), then
* add a `'remove_box_wrap' => true` to your metabox registration array.
*/
'remove_box_wrap' => false,
/*
* The following parameter is any additional arguments passed as $callback_args
* to add_meta_box, if/when applicable.
*
* CMB2 does not use these arguments in the add_meta_box callback, however, these args
* are parsed for certain special properties, like determining Gutenberg/block-editor
* compatibility.
*
* Examples:
*
* - Make sure default editor is used as metabox is not compatible with block editor
* [ '__block_editor_compatible_meta_box' => false/true ]
*
* - Or declare this box exists for backwards compatibility
* [ '__back_compat_meta_box' => false ]
*
* More: https://wordpress.org/gutenberg/handbook/extensibility/meta-box/
*/
'mb_callback_args' => null,
/*
* The following parameters are for options-page metaboxes,
* and several are passed along to add_menu_page()/add_submenu_page()
*/
// 'menu_title' => null, // Falls back to 'title' (above). Do not define here so we can set a fallback.
'message_cb' => '', // Optionally define the options-save message (via a callback).
'option_key' => '', // The actual option key and admin menu page slug.
'parent_slug' => '', // Used as first param in add_submenu_page().
'capability' => 'manage_options', // Cap required to view options-page.
'icon_url' => '', // Menu icon. Only applicable if 'parent_slug' is left empty.
'position' => null, // Menu position. Only applicable if 'parent_slug' is left empty.
'admin_menu_hook' => 'admin_menu', // Alternately 'network_admin_menu' to add network-level options page.
'display_cb' => false, // Override the options-page form output (CMB2_Hookup::options_page_output()).
'save_button' => '', // The text for the options-page save button. Defaults to 'Save'.
'disable_settings_errors' => false, // On settings pages (not options-general.php sub-pages), allows disabling.
'tab_group' => '', // Tab-group identifier, enables options page tab navigation.
// 'tab_title' => null, // Falls back to 'title' (above). Do not define here so we can set a fallback.
// 'autoload' => true, // Defaults to true, the options-page option will be autloaded.
);
/**
* Metabox field objects
*
* @var array
* @since 2.0.3
*/
protected $fields = array();
/**
* An array of hidden fields to output at the end of the form
*
* @var array
* @since 2.0.0
*/
protected $hidden_fields = array();
/**
* Array of key => value data for saving. Likely $_POST data.
*
* @var string
* @since 2.0.0
*/
protected $generated_nonce = '';
/**
* Whether there are fields to be shown in columns. Set in CMB2::add_field().
*
* @var bool
* @since 2.2.2
*/
protected $has_columns = false;
/**
* If taxonomy field is requesting to remove_default, we store the taxonomy here.
*
* @var array
* @since 2.2.3
*/
protected $tax_metaboxes_to_remove = array();
/**
* Get started
*
* @since 0.4.0
* @param array $config Metabox config array.
* @param integer $object_id Optional object id.
*/
public function __construct( $config, $object_id = 0 ) {
if ( empty( $config['id'] ) ) {
wp_die( esc_html__( 'Metabox configuration is required to have an ID parameter.', 'cmb2' ) );
}
$this->cmb_id = $config['id'];
$this->meta_box = wp_parse_args( $config, $this->mb_defaults );
$this->meta_box['fields'] = array();
// Ensures object_types is an array.
$this->set_prop( 'object_types', $this->box_types() );
$this->object_id( $object_id );
if ( $this->is_options_page_mb() ) {
// Check initial priority.
if ( empty( $config['priority'] ) ) {
// If not explicitly defined, Reset the priority to 10
// Fixes https://github.com/CMB2/CMB2/issues/1410.
$this->meta_box['priority'] = 10;
}
$this->init_options_mb();
}
$this->mb_object_type();
if ( ! empty( $config['fields'] ) && is_array( $config['fields'] ) ) {
$this->add_fields( $config['fields'] );
}
CMB2_Boxes::add( $this );
/**
* Hook during initiation of CMB2 object
*
* The dynamic portion of the hook name, $this->cmb_id, is this meta_box id.
*
* @param array $cmb This CMB2 object
*/
do_action( "cmb2_init_{$this->cmb_id}", $this );
// Hook in the hookup... how meta.
add_action( "cmb2_init_hookup_{$this->cmb_id}", array( 'CMB2_Hookup', 'maybe_init_and_hookup' ) );
// Hook in the rest api functionality.
add_action( "cmb2_init_hookup_{$this->cmb_id}", array( 'CMB2_REST', 'maybe_init_and_hookup' ) );
}
/**
* Loops through and displays fields
*
* @since 1.0.0
* @param int $object_id Object ID.
* @param string $object_type Type of object being saved. (e.g., post, user, or comment).
*
* @return CMB2
*/
public function show_form( $object_id = 0, $object_type = '' ) {
$this->render_form_open( $object_id, $object_type );
foreach ( $this->prop( 'fields' ) as $field_args ) {
$this->render_field( $field_args );
}
return $this->render_form_close( $object_id, $object_type );
}
/**
* Outputs the opening form markup and runs corresponding hooks:
* 'cmb2_before_form' and "cmb2_before_{$object_type}_form_{$this->cmb_id}"
*
* @since 2.2.0
* @param integer $object_id Object ID.
* @param string $object_type Object type.
*
* @return CMB2
*/
public function render_form_open( $object_id = 0, $object_type = '' ) {
$object_type = $this->object_type( $object_type );
$object_id = $this->object_id( $object_id );
echo "\n\n";
$this->nonce_field();
/**
* Hook before form table begins
*
* @param array $cmb_id The current box ID.
* @param int $object_id The ID of the current object.
* @param string $object_type The type of object you are working with.
* Usually `post` (this applies to all post-types).
* Could also be `comment`, `user` or `options-page`.
* @param array $cmb This CMB2 object.
*/
do_action( 'cmb2_before_form', $this->cmb_id, $object_id, $object_type, $this );
/**
* Hook before form table begins
*
* The first dynamic portion of the hook name, $object_type, is the type of object
* you are working with. Usually `post` (this applies to all post-types).
* Could also be `comment`, `user` or `options-page`.
*
* The second dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
*
* @param array $cmb_id The current box ID
* @param int $object_id The ID of the current object
* @param array $cmb This CMB2 object
*/
do_action( "cmb2_before_{$object_type}_form_{$this->cmb_id}", $object_id, $this );
echo '
';
return $this;
}
/**
* Defines the classes for the CMB2 form/wrap.
*
* @since 2.0.0
* @return string Space concatenated list of classes
*/
public function box_classes() {
$classes = array( 'cmb2-wrap', 'form-table' );
// Use the callback to fetch classes.
if ( $added_classes = $this->get_param_callback_result( 'classes_cb' ) ) {
$added_classes = is_array( $added_classes ) ? $added_classes : array( $added_classes );
$classes = array_merge( $classes, $added_classes );
}
if ( $added_classes = $this->prop( 'classes' ) ) {
$added_classes = is_array( $added_classes ) ? $added_classes : array( $added_classes );
$classes = array_merge( $classes, $added_classes );
}
/**
* Add our context classes for non-standard metaboxes.
*
* @since 2.2.4
*/
if ( $this->is_alternate_context_box() ) {
$context = array();
// Include custom class if requesting no title.
if ( ! $this->prop( 'title' ) && ! $this->prop( 'remove_box_wrap' ) ) {
$context[] = 'cmb2-context-wrap-no-title';
}
// Include a generic context wrapper.
$context[] = 'cmb2-context-wrap';
// Include a context-type based context wrapper.
$context[] = 'cmb2-context-wrap-' . $this->prop( 'context' );
// Include an ID based context wrapper as well.
$context[] = 'cmb2-context-wrap-' . $this->prop( 'id' );
// And merge all the classes back into the array.
$classes = array_merge( $classes, $context );
}
/**
* Globally filter box wrap classes
*
* @since 2.2.2
*
* @param string $classes Array of classes for the cmb2-wrap.
* @param CMB2 $cmb This CMB2 object.
*/
$classes = apply_filters( 'cmb2_wrap_classes', $classes, $this );
$split = array();
foreach ( array_filter( $classes ) as $class ) {
foreach ( explode( ' ', $class ) as $_class ) {
// Clean up & sanitize.
$split[] = sanitize_html_class( strip_tags( $_class ) );
}
}
$classes = $split;
// Remove any duplicates.
$classes = array_unique( $classes );
// Make it a string.
return implode( ' ', $classes );
}
/**
* Outputs the closing form markup and runs corresponding hooks:
* 'cmb2_after_form' and "cmb2_after_{$object_type}_form_{$this->cmb_id}"
*
* @since 2.2.0
* @param integer $object_id Object ID.
* @param string $object_type Object type.
*
* @return CMB2
*/
public function render_form_close( $object_id = 0, $object_type = '' ) {
$object_type = $this->object_type( $object_type );
$object_id = $this->object_id( $object_id );
echo '
';
$this->render_hidden_fields();
/**
* Hook after form form has been rendered
*
* The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
*
* The first dynamic portion of the hook name, $object_type, is the type of object
* you are working with. Usually `post` (this applies to all post-types).
* Could also be `comment`, `user` or `options-page`.
*
* @param int $object_id The ID of the current object
* @param array $cmb This CMB2 object
*/
do_action( "cmb2_after_{$object_type}_form_{$this->cmb_id}", $object_id, $this );
/**
* Hook after form form has been rendered
*
* @param array $cmb_id The current box ID.
* @param int $object_id The ID of the current object.
* @param string $object_type The type of object you are working with.
* Usually `post` (this applies to all post-types).
* Could also be `comment`, `user` or `options-page`.
* @param array $cmb This CMB2 object.
*/
do_action( 'cmb2_after_form', $this->cmb_id, $object_id, $object_type, $this );
echo "\n\n";
return $this;
}
/**
* Renders a field based on the field type
*
* @since 2.2.0
* @param array $field_args A field configuration array.
* @return mixed CMB2_Field object if successful.
*/
public function render_field( $field_args ) {
$field_args['context'] = $this->prop( 'context' );
if ( 'group' === $field_args['type'] ) {
if ( ! isset( $field_args['show_names'] ) ) {
$field_args['show_names'] = $this->prop( 'show_names' );
}
$field = $this->render_group( $field_args );
} elseif ( 'hidden' === $field_args['type'] && $this->get_field( $field_args )->should_show() ) {
// Save rendering for after the metabox.
$field = $this->add_hidden_field( $field_args );
} else {
$field_args['show_names'] = $this->prop( 'show_names' );
// Render default fields.
$field = $this->get_field( $field_args )->render_field();
}
return $field;
}
/**
* Render a group of fields.
*
* @param array|CMB2_Field $args Array of field arguments for a group field parent or the group parent field.
* @return CMB2_Field|null Group field object.
*/
public function render_group( $args ) {
$field_group = false;
if ( $args instanceof CMB2_Field ) {
$field_group = 'group' === $args->type() ? $args : false;
} elseif ( isset( $args['id'], $args['fields'] ) && is_array( $args['fields'] ) ) {
$field_group = $this->get_field( $args );
}
if ( ! $field_group ) {
return;
}
$field_group->render_context = 'edit';
$field_group->peform_param_callback( 'render_row_cb' );
return $field_group;
}
/**
* The default callback to render a group of fields.
*
* @since 2.2.6
*
* @param array $field_args Array of field arguments for the group field parent.
* @param CMB2_Field $field_group The CMB2_Field group object.
*
* @return CMB2_Field|null Group field object.
*/
public function render_group_callback( $field_args, $field_group ) {
// If field is requesting to be conditionally shown.
if ( ! $field_group || ! $field_group->should_show() ) {
return;
}
$field_group->index = 0;
$field_group->peform_param_callback( 'before_group' );
$desc = $field_group->args( 'description' );
$label = $field_group->args( 'name' );
$group_val = (array) $field_group->value();
echo '