[
  {
    "path": ".gitignore",
    "content": "\n# Created by https://www.gitignore.io/api/osx\n\n### OSX ###\n*.DS_Store\r\n.AppleDouble\r\n.LSOverride\r\n\r\n# Icon must end with two \\r\r\nIcon\r\n# Thumbnails\r\n._*\r\n# Files that might appear in the root of a volume\r\n.DocumentRevisions-V100\r\n.fseventsd\r\n.Spotlight-V100\r\n.TemporaryItems\r\n.Trashes\r\n.VolumeIcon.icns\r\n.com.apple.timemachine.donotpresent\r\n# Directories potentially created on remote AFP share\r\n.AppleDB\r\n.AppleDesktop\r\nNetwork Trash Folder\r\nTemporary Items\r\n.apdisk\r\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contribute To CMB2 Code Snippet Library\n\nThe CMB2 code snippet library is a community-maintained repository and so contributions are always welcome. Thanks to EDD for [the inspiration](https://github.com/easydigitaldownloads/library) for this library.\n\n## Submitting a Snippet\n\nTo submit a code library for inclusion in the library, please fork the repository and then submit a [pull request](https://github.com/jtsternberg/CMB2-Snippet-Library/pulls). Pippin has an excellent tutorial for [submitting your first pull request](http://pippinsplugins.com/submitting-your-first-pull-request/).\n\nGuidelines for submission:\n\n- Each snippet should be placed in it's own file.\n- Each file should be named appropriately based on the purpose of the snippet.\n- Files should be placed in the appropriate category (folder). If no category exists, you may create one with your pull request.\n- All functions and classes should be prefixed with your own unique prefix, preferrably one that contains your initials followed by `_cmb2_`. For example: `jt_cmb2_{function name here}`.\n- (optional) Add [WordPress plugin headers](http://codex.wordpress.org/File_Header#Plugin_File_Header_Example) to allow others to use your snippet as a plugin.\n"
  },
  {
    "path": "README.md",
    "content": "CMB2 Code Snippet Library\n========================\n\nThis is a community-maintained repository of code snippets that help modify the default behavior of [CMB2](https://github.com/WebDevStudios/CMB2/).\n\nSnippets are organized into categories (folders) and each snippet is placed in its own file with a name that describes what it does.\n\nThank you to the EDD team for [the inspiration](https://github.com/easydigitaldownloads/library) for this library.\n\n## Submitting Your Snippet\n\nWe welcome and encourage everyone to submit their code snippets. If you would like to submit your snippet, please [fork](https://github.com/WebDevStudios/CMB2-Snippet-Library/fork) the repository and then create a [pull request](https://github.com/WebDevStudios/CMB2-Snippet-Library/compare/).\n\nPlease refer to the [Contributing guidelines](https://github.com/WebDevStudios/CMB2-Snippet-Library/blob/master/CONTRIBUTING.md) before submitting your pull request.\n\n## Proposing a Snippet\n\nIf there is a snippet you are unable to find and would like to propose get written, please open an [issue](https://github.com/WebDevStudios/CMB2-Snippet-Library/issues) and describe your need.\n\n## Notes\n\nThis is not a tutorial archive. Please see the [CMB2 github repo wiki](https://github.com/WebDevStudios/CMB2/wiki) if you're looking for official documentation and tutorials.\n\nThese snippets come with no guarantees. Due to the constant evolution of the CMB2 codebase, it is possible that the behavior of these snippets may change overtime. Find something broken? [Let us know](https://github.com/WebDevStudios/CMB2-Snippet-Library/issues)!"
  },
  {
    "path": "api/README.md",
    "content": "CMB2 API Snippets\n==========\n\nSnippets related to the CMB2 API endpoints/functionality.\n"
  },
  {
    "path": "api/cmb2-file-list-ordered.php",
    "content": "<?php\n\n/**\n * By default, when fetching the value for the file_list field via the REST API, the order is\n * obliterated when the data is converted to JSON via json_encode. This replaces the default value\n * of the file_list field with an array of arrays, which will preserve the orginal order.\n *\n * @see https://github.com/CMB2/CMB2/issues/1150\n */\n\n/**\n * Returns an array of file arrays with id/url paramaters, which preserves the original file ordering.\n *\n * @param mixed      $value The value from CMB2_Field::get_data()\n * @param CMB2_Field $field The field object.\n */\nfunction yourprefix_cmb2_ordered_file_list_array_in_api( $value, $field ) {\n\tif ( ! empty( $value ) && is_array( $value ) ) {\n\t\t$files = $value;\n\t\t$value = array();\n\t\tforeach ( $files as $file_id => $file_url ) {\n\t\t\t$value[] = array(\n\t\t\t\t'id' => $file_id,\n\t\t\t\t'url' => $file_url,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn $value;\n}\n\n/**\n * Filters the value before it is sent to the REST request.\n *\n * \"_yourprefix_demo_file_list\" is a dynamic portion of the hook name, referring to the field id.\n */\nadd_filter( 'cmb2_get_rest_value_for__yourprefix_demo_file_list', 'yourprefix_cmb2_ordered_file_list_array_in_api', 10, 2 );\n\nreturn;\n\n// Another method to make this modification to several different file_list fields.\n// Do not use both of these methods. Pick one.\n\ndefine( 'YOURPREFIX_FILE_LIST_IDS', array(\n\t'_yourprefix_demo_file_list',\n\t'xyz'\n\t// etc.\n) );\n\nfunction yourprefix_cmb2_ordered_file_list_array_in_api_2( $value, $field ) {\n\t// Replace\n\tif ( in_array( $field->_id(), YOURPREFIX_FILE_LIST_IDS, true ) && ! empty( $value ) && is_array( $value ) ) {\n\t\t$files = $value;\n\t\t$value = array();\n\t\tforeach ( $files as $file_id => $file_url ) {\n\t\t\t$value[] = array(\n\t\t\t\t'id' => $file_id,\n\t\t\t\t'url' => $file_url,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn $value;\n}\nadd_filter( 'cmb2_get_rest_value_file_list', 'yourprefix_cmb2_ordered_file_list_array_in_api_2', 10, 2 );"
  },
  {
    "path": "conditional-display/exclude-for-ids.php",
    "content": "<?php\n/**\n * Allow metabox to show up everywhere except a specified list of page IDs.\n * @link https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-show_on-filters#example-exclude-on-ids wiki\n */\n\n$cmb = new_cmb2_box( array(\n\t'id'           => 'exclude_for_ids',\n\t'title'        => 'Demo',\n\t'exclude_ids' => array( 1, 2, 3, 55 ), // Exclude metabox on these post-ids\n\t'show_on_cb' => 'cmb2_exclude_for_ids', // function should return a bool value\n) );\n\n/**\n * Exclude metabox on specific IDs\n * @param  object $cmb CMB2 object\n * @return bool        True/false whether to show the metabox\n */\nfunction cmb2_exclude_for_ids( $cmb ) {\n\t$ids_to_exclude = $cmb->prop( 'exclude_ids', array() );\n\t$excluded = in_array( $cmb->object_id(), $ids_to_exclude, true );\n\n\treturn ! $excluded;\n}\n"
  },
  {
    "path": "conditional-display/hide-on-new-post-page.php",
    "content": "<?php\n/**\n * Remove metabox from appearing on post new screens before the post has been saved.\n * Can also add additional screens to exclude via the `'exclude_from'` property.\n * @link https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-show_on-filters#example-exclude-on-new-post-screens wiki\n */\n\n$cmb = new_cmb2_box( array(\n\t'id'           => 'exclude_for_ids',\n\t'title'        => 'Demo',\n\t'exclude_from' => array( 'post-new.php' ), // Exclude metabox on new-post screen\n\t'show_on_cb'   => 'tgm_exclude_from_new', // function should return a bool value\n) );\n\n/**\n * Removes metabox from appearing on post new screens before the post\n * ID has been set.\n * @author Thomas Griffin\n * @param  object $cmb CMB2 object\n * @return bool        True/false whether to show the metabox\n */\nfunction tgm_exclude_from_new( $cmb ) {\n\tglobal $pagenow;\n\n\t$exclude_from = $cmb->prop( 'exclude_from', array( 'post-new.php' ) );\n\t$excluded = in_array( $pagenow, $exclude_from, true );\n\n\treturn ! $excluded;\n}\n"
  },
  {
    "path": "conditional-display/show-for-taxonomy-terms.php",
    "content": "<?php\n/**\n * This allows you to specify one or more taxonomies, and for each taxonomy one or more terms.\n * If a post is tagged one of those terms, this metabox shows up on its Edit screen.\n * @link https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-show_on-filters#example-taxonomy-show_on-filter wiki\n */\n\n$cmb = new_cmb2_box( array(\n\t'id'         => 'show_for_taxonomy_terms',\n\t'title'      => 'Demo',\n\t'show_on_cb' => 'be_taxonomy_show_on_filter', // function should return a bool value\n\t'show_on_terms' => array(\n\t\t'category' => array( 'featured' ),\n\t\t'post_tag' => array( 'best-of' ),\n\t),\n) );\n\n/**\n * Taxonomy show_on filter\n * @author Bill Erickson\n * @param  object $cmb CMB2 object\n * @return bool        True/false whether to show the metabox\n */\nfunction be_taxonomy_show_on_filter( $cmb ) {\n\t$tax_terms_to_show_on = $cmb->prop( 'show_on_terms', array() );\n\tif ( empty( $tax_terms_to_show_on ) || ! $cmb->object_id() ) {\n\t\treturn false;\n\t}\n\n\t$post_id = $cmb->object_id();\n\t$post = get_post( $post_id );\n\n\tforeach( (array) $tax_terms_to_show_on as $taxonomy => $slugs ) {\n\t\tif ( ! is_array( $slugs ) ) {\n\t\t\t$slugs = array( $slugs );\n\t\t}\n\n\t\t$terms = $post\n\t\t\t? get_the_terms( $post, $taxonomy )\n\t\t\t: wp_get_object_terms( $post_id, $taxonomy );\n\n\t\tif ( ! empty( $terms ) ) {\n\t\t\tforeach( $terms as $term ) {\n\t\t\t\tif ( in_array( $term->slug, $slugs, true ) ) {\n\t\t\t\t\twp_die( '<xmp>: '. print_r( 'show it', true ) .'</xmp>' );\n\t\t\t\t\t// Ok, show this metabox\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n"
  },
  {
    "path": "conditional-display/show-if-matching-meta-value.php",
    "content": "<?php\n/**\n * Only show contact metabox if status set to external.\n * @link https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-show_on-filters#examples wiki\n */\n\nadd_action( 'cmb2_admin_init', 'cmb2_register_conditional_metabox' );\n/**\n * Hook in and add a demo metabox. Can only happen on the 'cmb2_admin_init' or 'cmb2_init' hook.\n */\nfunction cmb2_register_conditional_metabox() {\n\t/**\n\t * Metabox to save the 'status' where 'Internal' is the default.\n\t */\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'wiki_status_metabox',\n\t\t'title'        => 'Status Metabox',\n\t\t'object_types' => array( 'page', ), // Post type\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name'    => 'Status',\n\t\t'id'      => 'wiki_status',\n\t\t'type'    => 'select',\n\t\t'default' => 'internal',\n\t\t'options' => array(\n\t\t\t'internal' => 'Internal',\n\t\t\t'external' => 'External',\n\t\t),\n\t) );\n\n\t/**\n\t * Metabox to conditionally display if the 'status' is set to 'External'.\n\t */\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'wiki_conditonal_metabox',\n\t\t'title'        => 'Contact Info',\n\t\t'object_types' => array( 'page', ), // Post type\n\t\t'show_on_cb' => 'cmb_only_show_for_external', // function should return a bool value\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name'       => 'Email',\n\t\t'id'         => 'wiki_email',\n\t\t'type'       => 'text_email',\n\t) );\n}\n\n/**\n * Only display a metabox if the page's 'status' is 'external'\n * @param  object $cmb CMB2 object\n * @return bool        True/false whether to show the metabox\n */\nfunction cmb_only_show_for_external( $cmb ) {\n\t$status = get_post_meta( $cmb->object_id(), 'wiki_status', 1 );\n\n\t// Only show if status is 'external'\n\treturn 'external' === $status;\n}\n"
  },
  {
    "path": "conditional-display/show-only-for-top-level-posts.php",
    "content": "<?php\n/**\n * Exclude metabox on non top level posts\n * @link https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-show_on-filters#example-exclude-on-non-top-level-posts wiki\n */\n\n$cmb = new_cmb2_box( array(\n\t'id'         => 'exclude_for_ids',\n\t'title'      => 'Demo',\n\t'show_on_cb' => 'ba_metabox_add_for_top_level_posts_only', // function should return a bool value\n) );\n\n/**\n * Exclude metabox on non top level posts\n * @author Travis Northcutt\n * @param  object $cmb CMB2 object\n * @return bool        True/false whether to show the metabox\n */\nfunction ba_metabox_add_for_top_level_posts_only( $cmb ) {\n\t$has_parent = $cmb->object_id() && get_post_ancestors( $cmb->object_id() );\n\n\treturn ! $has_parent;\n}\n"
  },
  {
    "path": "custom-field-types/README.md",
    "content": "Custom Field Types\n==========\n\nSnippets for [adding custom CMB2 field types](https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-field-types)."
  },
  {
    "path": "custom-field-types/address-field-type/address-field-type.php",
    "content": "<?php\n/*\n * Plugin Name: CMB2 Custom Field Type - Address\n * Description: Makes available an 'address' CMB2 Custom Field Type. Based on https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-field-types#example-4-multiple-inputs-one-field-lets-create-an-address-field\n * Author: jtsternberg\n * Author URI: http://dsgnwrks.pro\n * Version: 0.1.0\n */\n\n/**\n * Template tag for displaying an address from the CMB2 address field type (on the front-end)\n *\n * @since  0.1.0\n *\n * @param  string  $metakey The 'id' of the 'address' field (the metakey for get_post_meta)\n * @param  integer $post_id (optional) post ID. If using in the loop, it is not necessary\n */\nfunction jt_cmb2_address_field( $metakey, $post_id = 0 ) {\n\techo jt_cmb2_get_address_field( $metakey, $post_id );\n}\n\n/**\n * Template tag for returning an address from the CMB2 address field type (on the front-end)\n *\n * @since  0.1.0\n *\n * @param  string  $metakey The 'id' of the 'address' field (the metakey for get_post_meta)\n * @param  integer $post_id (optional) post ID. If using in the loop, it is not necessary\n */\nfunction jt_cmb2_get_address_field( $metakey, $post_id = 0 ) {\n\t$post_id = $post_id ? $post_id : get_the_ID();\n\t$address = get_post_meta( $post_id, $metakey, 1 );\n\n\t// Set default values for each address key\n\t$address = wp_parse_args( $address, array(\n\t\t'address-1' => '',\n\t\t'address-2' => '',\n\t\t'city'      => '',\n\t\t'state'     => '',\n\t\t'zip'       => '',\n\t\t'country'   => '',\n\t) );\n\n\t$output = '<div class=\"cmb2-address\">';\n\t$output .= '<p><strong>Address:</strong> ' . esc_html( $address['address-1'] ) . '</p>';\n\tif ( $address['address-2'] ) {\n\t\t$output .= '<p>' . esc_html( $address['address-2'] ) . '</p>';\n\t}\n\t$output .= '<p><strong>City:</strong> ' . esc_html( $address['city'] ) . '</p>';\n\t$output .= '<p><strong>State:</strong> ' . esc_html( $address['state'] ) . '</p>';\n\t$output .= '<p><strong>Zip:</strong> ' . esc_html( $address['zip'] ) . '</p>';\n\t$output .= '</div><!-- .cmb2-address -->';\n\n\treturn apply_filters( 'jt_cmb2_get_address_field', $output );\n}\n\nfunction cmb2_init_address_field() {\n\trequire_once dirname( __FILE__ ) . '/class-cmb2-render-address-field.php';\n\tCMB2_Render_Address_Field::init();\n}\nadd_action( 'cmb2_init', 'cmb2_init_address_field' );\n"
  },
  {
    "path": "custom-field-types/address-field-type/class-cmb2-render-address-field.php",
    "content": "<?php\n\n/**\n * Handles 'address' custom field type.\n */\nclass CMB2_Render_Address_Field extends CMB2_Type_Base {\n\n\t/**\n\t * List of states. To translate, pass array of states in the 'state_list' field param.\n\t *\n\t * @var array\n\t */\n\tprotected static $state_list = array( 'AL' => 'Alabama', 'AK' => 'Alaska', 'AZ' => 'Arizona', 'AR' => 'Arkansas', 'CA' => 'California', 'CO' => 'Colorado', 'CT' => 'Connecticut', 'DE' => 'Delaware', 'DC' => 'District Of Columbia', 'FL' => 'Florida', 'GA' => 'Georgia', 'HI' => 'Hawaii', 'ID' => 'Idaho', 'IL' => 'Illinois', 'IN' => 'Indiana', 'IA' => 'Iowa', 'KS' => 'Kansas', 'KY' => 'Kentucky', 'LA' => 'Louisiana', 'ME' => 'Maine', 'MD' => 'Maryland', 'MA' => 'Massachusetts', 'MI' => 'Michigan', 'MN' => 'Minnesota', 'MS' => 'Mississippi', 'MO' => 'Missouri', 'MT' => 'Montana', 'NE' => 'Nebraska', 'NV' => 'Nevada', 'NH' => 'New Hampshire', 'NJ' => 'New Jersey', 'NM' => 'New Mexico', 'NY' => 'New York', 'NC' => 'North Carolina', 'ND' => 'North Dakota', 'OH' => 'Ohio', 'OK' => 'Oklahoma', 'OR' => 'Oregon', 'PA' => 'Pennsylvania', 'RI' => 'Rhode Island', 'SC' => 'South Carolina', 'SD' => 'South Dakota', 'TN' => 'Tennessee', 'TX' => 'Texas', 'UT' => 'Utah', 'VT' => 'Vermont', 'VA' => 'Virginia', 'WA' => 'Washington', 'WV' => 'West Virginia', 'WI' => 'Wisconsin', 'WY' => 'Wyoming' );\n\n\tpublic static function init() {\n\t\tadd_filter( 'cmb2_render_class_address', array( __CLASS__, 'class_name' ) );\n\t\tadd_filter( 'cmb2_sanitize_address', array( __CLASS__, 'maybe_save_split_values' ), 12, 4 );\n\n\t\t/**\n\t\t * The following snippets are required for allowing the address field\n\t\t * to work as a repeatable field, or in a repeatable group.\n\t\t */\n\t\tadd_filter( 'cmb2_sanitize_address', array( __CLASS__, 'sanitize' ), 10, 5 );\n\t\tadd_filter( 'cmb2_types_esc_address', array( __CLASS__, 'escape' ), 10, 4 );\n\t\tadd_filter( 'cmb2_override_meta_value', array( __CLASS__, 'get_split_meta_value' ), 12, 4 );\n\t}\n\n\tpublic static function class_name() { return __CLASS__; }\n\n\t/**\n\t * Handles outputting the address field.\n\t */\n\tpublic function render() {\n\n\t\t// make sure we assign each part of the value we need.\n\t\t$value = wp_parse_args( $this->field->escaped_value(), array(\n\t\t\t'address-1' => '',\n\t\t\t'address-2' => '',\n\t\t\t'city'      => '',\n\t\t\t'state'     => '',\n\t\t\t'zip'       => '',\n\t\t\t'country'   => '',\n\t\t) );\n\n\t\tif ( ! $this->field->args( 'do_country' ) ) {\n\t\t\t$state_list = $this->field->args( 'state_list', array() );\n\t\t\tif ( empty( $state_list ) ) {\n\t\t\t\t$state_list = self::$state_list;\n\t\t\t}\n\n\t\t\t// Add the \"label\" option. Can override via the field text param.\n\t\t\t$state_list = array( '' => esc_html( $this->_text( 'address_select_state_text', 'Select a State' ) ) ) + $state_list;\n\n\t\t\t$state_options = '';\n\t\t\tforeach ( $state_list as $abrev => $state ) {\n\t\t\t\t$state_options .= '<option value=\"' . $abrev . '\" ' . selected( $value['state'], $abrev, false ) . '>' . $state . '</option>';\n\t\t\t}\n\t\t}\n\n\t\t$state_label = 'State';\n\t\tif ( $this->field->args( 'do_country' ) ) {\n\t\t\t$state_label .= '/Province';\n\t\t}\n\n\t\tob_start();\n\t\t// Do html.\n\t\t?>\n\t\t<div><p><label for=\"<?php echo $this->_id( '_address_1', false ); ?>\"><?php echo esc_html( $this->_text( 'address_address_1_text', 'Address 1' ) ); ?></label></p>\n\t\t\t<?php echo $this->types->input( array(\n\t\t\t\t'name'  => $this->_name( '[address-1]' ),\n\t\t\t\t'id'    => $this->_id( '_address_1' ),\n\t\t\t\t'value' => $value['address-1'],\n\t\t\t\t'desc'  => '',\n\t\t\t) ); ?>\n\t\t</div>\n\t\t<div><p><label for=\"<?php echo $this->_id( '_address_2', false ); ?>'\"><?php echo esc_html( $this->_text( 'address_address_2_text', 'Address 2' ) ); ?></label></p>\n\t\t\t<?php echo $this->types->input( array(\n\t\t\t\t'name'  => $this->_name( '[address-2]' ),\n\t\t\t\t'id'    => $this->_id( '_address_2' ),\n\t\t\t\t'value' => $value['address-2'],\n\t\t\t\t'desc'  => '',\n\t\t\t) ); ?>\n\t\t</div>\n\t\t<div style=\"overflow: hidden;\">\n\t\t\t<div class=\"alignleft\"><p><label for=\"<?php echo $this->_id( '_city', false ); ?>'\"><?php echo esc_html( $this->_text( 'address_city_text', 'City' ) ); ?></label></p>\n\t\t\t\t<?php echo $this->types->input( array(\n\t\t\t\t\t'class' => 'cmb_text_small',\n\t\t\t\t\t'name'  => $this->_name( '[city]' ),\n\t\t\t\t\t'id'    => $this->_id( '_city' ),\n\t\t\t\t\t'value' => $value['city'],\n\t\t\t\t\t'desc'  => '',\n\t\t\t\t) ); ?>\n\t\t\t</div>\n\t\t\t<div class=\"alignleft\"><p><label for=\"<?php echo $this->_id( '_state', false ); ?>'\"><?php echo esc_html( $this->_text( 'address_state_text', $state_label ) ); ?></label></p>\n\t\t\t\t<?php if ( $this->field->args( 'do_country' ) ) : ?>\n\t\t\t\t\t<?php echo $this->types->input( array(\n\t\t\t\t\t\t'class' => 'cmb_text_small',\n\t\t\t\t\t\t'name'  => $this->_name( '[state]' ),\n\t\t\t\t\t\t'id'    => $this->_id( '_state' ),\n\t\t\t\t\t\t'value' => $value['state'],\n\t\t\t\t\t\t'desc'  => '',\n\t\t\t\t\t) ); ?>\n\t\t\t\t<?php else : ?>\n\t\t\t\t\t<?php echo $this->types->select( array(\n\t\t\t\t\t\t'name'    => $this->_name( '[state]' ),\n\t\t\t\t\t\t'id'      => $this->_id( '_state' ),\n\t\t\t\t\t\t'options' => $state_options,\n\t\t\t\t\t\t'desc'    => '',\n\t\t\t\t\t) ); ?>\n\t\t\t\t<?php endif; ?>\n\t\t\t</div>\n\t\t\t<div class=\"alignleft\"><p><label for=\"<?php echo $this->_id( '_zip', false ); ?>'\"><?php echo esc_html( $this->_text( 'address_zip_text', 'Zip' ) ); ?></label></p>\n\t\t\t\t<?php echo $this->types->input( array(\n\t\t\t\t\t'class' => 'cmb_text_small',\n\t\t\t\t\t'name'  => $this->_name( '[zip]' ),\n\t\t\t\t\t'id'    => $this->_id( '_zip' ),\n\t\t\t\t\t'value' => $value['zip'],\n\t\t\t\t\t'type'  => 'number',\n\t\t\t\t\t'desc'  => '',\n\t\t\t\t) ); ?>\n\t\t\t</div>\n\t\t</div>\n\t\t<?php if ( $this->field->args( 'do_country' ) ) : ?>\n\t\t<div class=\"clear\"><p><label for=\"<?php echo $this->_id( '_country', false ); ?>'\"><?php echo esc_html( $this->_text( 'address_country_text', 'Country' ) ); ?></label></p>\n\t\t\t<?php echo $this->types->input( array(\n\t\t\t\t'name'  => $this->_name( '[country]' ),\n\t\t\t\t'id'    => $this->_id( '_country' ),\n\t\t\t\t'value' => $value['country'],\n\t\t\t\t'desc'  => '',\n\t\t\t) ); ?>\n\t\t</div>\n\t\t<?php endif; ?>\n\t\t<p class=\"clear\">\n\t\t\t<?php echo $this->_desc();?>\n\t\t</p>\n\t\t<?php\n\n\t\t// grab the data from the output buffer.\n\t\treturn $this->rendered( ob_get_clean() );\n\t}\n\n\t/**\n\t * Optionally save the Address values into separate fields\n\t */\n\tpublic static function maybe_save_split_values( $override_value, $value, $object_id, $field_args ) {\n\t\tif ( ! isset( $field_args['split_values'] ) || ! $field_args['split_values'] ) {\n\t\t\t// Don't do the override.\n\t\t\treturn $override_value;\n\t\t}\n\n\t\t$address_keys = array( 'address-1', 'address-2', 'city', 'state', 'zip' );\n\n\t\tforeach ( $address_keys as $key ) {\n\t\t\tif ( ! empty( $value[ $key ] ) ) {\n\t\t\t\tupdate_post_meta( $object_id, $field_args['id'] . 'addr_' . $key, sanitize_text_field( $value[ $key ] ) );\n\t\t\t}\n\t\t}\n\n\t\tremove_filter( 'cmb2_sanitize_address', array( __CLASS__, 'sanitize' ), 10, 5 );\n\n\t\t// Tell CMB2 we already did the update.\n\t\treturn true;\n\t}\n\n\tpublic static function sanitize( $check, $meta_value, $object_id, $field_args, $sanitize_object ) {\n\n\t\t// if not repeatable, bail out.\n\t\tif ( ! is_array( $meta_value ) || ! $field_args['repeatable'] ) {\n\t\t\treturn $check;\n\t\t}\n\n\t\tforeach ( $meta_value as $key => $val ) {\n\t\t\t$meta_value[ $key ] = array_filter( array_map( 'sanitize_text_field', $val ) );\n\t\t}\n\n\t\treturn array_filter( $meta_value );\n\t}\n\n\tpublic static function escape( $check, $meta_value, $field_args, $field_object ) {\n\t\t// if not repeatable, bail out.\n\t\tif ( ! is_array( $meta_value ) || ! $field_args['repeatable'] ) {\n\t\t\treturn $check;\n\t\t}\n\n\t\tforeach ( $meta_value as $key => $val ) {\n\t\t\t$meta_value[ $key ] = array_filter( array_map( 'esc_attr', $val ) );\n\t\t}\n\n\t\treturn array_filter( $meta_value );\n\t}\n\n\tpublic static function get_split_meta_value( $data, $object_id, $field_args, $field ) {\n\t\tif ( 'address' !== $field->args['type'] ) {\n\t\t\treturn $data;\n\t\t}\n\t\tif ( ! isset( $field->args['split_values'] ) || ! $field->args['split_values'] ) {\n\t\t\t// Don't do the override.\n\t\t\treturn $data;\n\t\t}\n\n\t\t$prefix = $field->args['id'] . 'addr_';\n\t\t// Construct an array to iterate to fetch individual meta values for our override.\n\t\t// Should match the values in the render() method.\n\t\t$metakeys = array(\n\t\t\t'address-1',\n\t\t\t'address-2',\n\t\t\t'city',\n\t\t\t'state',\n\t\t\t'zip',\n\t\t\t'country',\n\t\t);\n\n\t\t$newdata = array();\n\t\tforeach ( $metakeys as $metakey ) {\n\t\t\t// Use our prefix to construct the whole meta key from the postmeta table.\n\t\t\t$newdata[ $metakey ] = get_post_meta( $object_id, $prefix . $metakey, true );\n\t\t}\n\n\t\treturn $newdata;\n\t}\n}\n"
  },
  {
    "path": "custom-field-types/associate-wp-menu-field.php",
    "content": "<?php\n/**\n * Associates a WP menu with a post, and provides links between the post/menu editing interfaces.\n * Screenshots:\n * - metabox: http://b.ustin.co/Gxhf\n * - widget: http://b.ustin.co/lD30\n */\n\nfunction cmb2_register_post_menu_box() {\n\n\t$menu_assoc = CMB2_Post_Menu_Association::get_instance();\n\t$menu_assoc->menu_types    = array( 'post', 'page' );\n\t$menu_assoc->metabox_id    = 'associated_post_menu_box';\n\t$menu_assoc->menu_meta_key = 'associated_post_menu';\n\n\tif ( is_admin() ) {\n\t\t// Register menu association metabox to post types\n\t\t$cmb = $menu_assoc->register_post_menu_box();\n\t}\n}\nadd_action( 'cmb2_init', 'cmb2_register_post_menu_box' );\n\nclass CMB2_Post_Menu_Association {\n\n\tpublic $menu_types    = array();\n\tpublic $metabox_id    = '';\n\tpublic $menu_meta_key = '';\n\tpublic $menu_title_meta_key = '';\n\n\tprotected static $single_instance = null;\n\tprotected $associated_post        = null;\n\tprotected $menu_id                = 0;\n\n\t/**\n\t * Creates or returns an instance of this class.\n\t * @since  0.1.0\n\t * @return CMB2_Post_Menu_Association A single instance of this class.\n\t */\n\tpublic static function get_instance() {\n\t\tif ( null === self::$single_instance ) {\n\t\t\tself::$single_instance = new self();\n\t\t}\n\n\t\treturn self::$single_instance;\n\t}\n\n\tprotected function __construct() {\n\t\tadd_action( 'all_admin_notices', array( $this, 'associate_menu_post_message' ) );\n\t\tadd_action( 'wp_create_nav_menu', array( $this, 'set_menu_to_post' ) );\n\t}\n\n\tpublic function register_post_menu_box() {\n\t\treturn new_cmb2_box( $this->register_post_menu_box_args() );\n\t}\n\n\tpublic function register_post_menu_box_args() {\n\t\t$title_key = $this->menu_title_meta_key();\n\t\treturn apply_filters( 'register_post_menu_box_args', array(\n\t\t\t'id'           => $this->metabox_id,\n\t\t\t'title'        => 'Associated Menu',\n\t\t\t'object_types' => $this->menu_types, // Post type\n\t\t\t'context'      => 'side',\n\t\t\t'priority'     => 'low',\n\t\t\t'fields'       => array(\n\t\t\t\t$title_key => array(\n\t\t\t\t\t'name' => 'Menu Widget Title',\n\t\t\t\t\t'id'   => $title_key,\n\t\t\t\t\t'type' => 'text',\n\t\t\t\t),\n\t\t\t\t$this->menu_meta_key => array(\n\t\t\t\t\t'desc'         => 'If no menu is selected, associated menu widget will not show.',\n\t\t\t\t\t'id'           => $this->menu_meta_key,\n\t\t\t\t\t'type'         => 'select',\n\t\t\t\t\t'options_cb'   => array( $this, 'get_menus_list_options' ),\n\t\t\t\t\t'after'        => array( $this, 'post_menu_edit_or_create_link' ),\n\t\t\t\t),\n\t\t\t),\n\t\t) );\n\t}\n\n\tpublic function get_menus_list_options() {\n\t\t$menus = wp_get_nav_menus();\n\t\tif ( ! empty( $menus ) ) {\n\t\t\t$menus = wp_list_pluck( $menus, 'name', 'term_id' );\n\t\t} else {\n\t\t\t$menus = array();\n\t\t}\n\n\t\t$menus = array( '' => 'Select Menu' ) + $menus;\n\t\treturn $menus;\n\t}\n\n\tfunction post_menu_edit_or_create_link( $args, $field ) {\n\t\tstatic $script_added = false;\n\n\t\t$menu_id = get_post_meta( $field->object_id(), $this->menu_meta_key, 1 );\n\n\t\t$url = admin_url( '/nav-menus.php?action=edit&menu=' . absint( $menu_id ) . '&post_association=' . $field->object_id() );\n\t\t$link_title = $menu_id ? 'Edit Selected Menu' : 'Create New Menu';\n\n\t\t?>\n\t\t<a href=\"<?php echo esc_url( $url ); ?>\"><?php echo $link_title; ?></a>\n\n\t\t<p class=\"cmb2-metabox-description explain-widget-necessary\"><strong>Note:</strong> In order for this menu to display, you will need to ensure the \"Associated Post Menu\" widget is placed in the <a href=\"<?php echo admin_url( 'widgets.php' ); ?>\">widget area</a> for this template.</p>\n\n\t\t<?php if ( ! $script_added ) : ?>\n\t\t\t<script type=\"text/javascript\">\n\t\t\t\tif ( '#<?php echo $this->metabox_id; ?>' === window.location.hash ) {\n\t\t\t\t\tvar el = document.getElementById( '<?php echo $this->metabox_id; ?>' );\n\t\t\t\t\tel.className += ' post-menu-box-highlighted';\n\t\t\t\t}\n\t\t\t</script>\n\t\t\t<style type=\"text/css\">\n\t\t\t\t.post-menu-box-highlighted {\n\t\t\t\t\tbox-shadow: 0 0 10px 5px rgba(226, 73, 73, 0.28);\n\t\t\t\t}\n\t\t\t\t#side-sortables #<?php echo $this->metabox_id; ?> .explain-widget-necessary {\n\t\t\t\t\tpadding-bottom: 0;\n\t\t\t\t\tpadding-top: 10px;\n\t\t\t\t\tmargin-bottom: -15px;\n\t\t\t\t}\n\t\t\t</style>\n\t\t<?php endif;\n\n\t\t$script_added = true;\n\t}\n\n\tpublic function associate_menu_post_message() {\n\t\tif ( empty( $_REQUEST['post_association'] ) || ! $this->menu_meta_key ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( empty( $_REQUEST['menu'] ) ) {\n\t\t\t$this->associate_menu_message( $_REQUEST['post_association'], 'When created, this Menu will be set as the %s Menu for: <strong><a href=\"%s\">%s</a></strong> (%d)' );\n\t\t} else {\n\t\t\t$this->associate_menu_message( $_REQUEST['post_association'], 'This Menu is set as the %s Menu for: <strong><a href=\"%s\">%s</a></strong> (%d)' );\n\t\t}\n\t}\n\n\tpublic function associate_menu_message( $post_id, $message ) {\n\t\tglobal $pagenow;\n\t\tif ( empty( $post_id ) || 'nav-menus.php' !== $pagenow ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$post = get_post( absint( $post_id ) );\n\n\t\tif ( empty( $post ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$pt = get_post_type_object( $post->post_type );\n\n\t\tprintf(\n\t\t\t'<div id=\"message\" class=\"updated\"><p>%s</p></div>',\n\t\t\tsprintf(\n\t\t\t\t$message,\n\t\t\t\t$pt->labels->singular_name,\n\t\t\t\tget_edit_post_link( $post->ID ) .'#'. $this->metabox_id,\n\t\t\t\tget_the_title( $post->ID ),\n\t\t\t\t$post->ID\n\t\t\t)\n\t\t);\n\t}\n\n\tpublic function set_menu_to_post( $menu_id ) {\n\t\tif ( empty( $_REQUEST['post_association'] ) || empty( $menu_id ) || ! $this->menu_meta_key ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$post = get_post( absint( $_REQUEST['post_association']  ) );\n\n\t\tif ( empty( $post ) || ! in_array( $post->post_type, $this->menu_types, true ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$this->menu_id = $menu_id;\n\t\t$this->associated_post = $post;\n\n\t\tupdate_post_meta( $post->ID, $this->menu_meta_key, $menu_id );\n\n\t\t// Modify the post-menu-save redirect to add our query var.\n\t\tadd_filter( 'wp_redirect', array( $this, 'redirect_with_post_association' ) );\n\t}\n\n\tpublic function redirect_with_post_association( $location ) {\n\t\tif ( $this->menu_id && $location === admin_url( 'nav-menus.php?menu='. $this->menu_id ) ) {\n\t\t\t// Add our associated post id query var.\n\t\t\t$location = add_query_arg( 'post_association', $this->associated_post->ID, $location );\n\t\t}\n\n\t\treturn $location;\n\t}\n\n\tpublic function menu_title_meta_key() {\n\t\treturn $this->menu_title_meta_key ? $this->menu_title_meta_key : $this->menu_meta_key . '_widget_title';\n\t}\n\n}\nCMB2_Post_Menu_Association::get_instance();\n\n/**\n * Handles the Post Menu Widget\n *\n * @since 3.0.0\n *\n * @see WP_Widget\n */\nclass CMB2_Post_Menu_Widget extends WP_Widget {\n\n\t/**\n\t * Sets up a new Associated Post Menu widget instance.\n\t *\n\t * @since 3.0.0\n\t * @access public\n\t */\n\tpublic function __construct() {\n\t\t$widget_ops = array(\n\t\t\t'customize_selective_refresh' => true,\n\t\t\t'description' => 'Shows an associated custom menu in your sidebar, if it is set.',\n\t\t\t'classname'   => 'associated-post-menu',\n\t\t);\n\t\tparent::__construct( 'associated-post-menu', 'Associated Post Menu', $widget_ops );\n\t}\n\n\t/**\n\t * Outputs the content for the associated Menu widget instance.\n\t *\n\t * @since 3.0.0\n\t * @access public\n\t *\n\t * @param array $args     Display arguments including 'before_title', 'after_title',\n\t *                        'before_widget', and 'after_widget'.\n\t * @param array $instance Settings for the current Associated Post Menu widget instance.\n\t */\n\tpublic function widget( $args, $instance ) {\n\t\t// Get menu\n\t\t$menu_id = absint( get_post_meta( get_the_ID(), CMB2_Post_Menu_Association::get_instance()->menu_meta_key, 1 ) );\n\t\t$nav_menu = ! empty( $menu_id ) ? wp_get_nav_menu_object( $menu_id ) : false;\n\n\t\tif ( ! $nav_menu ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$widget_title = get_post_meta( get_the_ID(), CMB2_Post_Menu_Association::get_instance()->menu_title_meta_key(), 1 );\n\t\tif ( empty( $widget_title ) ) {\n\t\t\t$widget_title = isset( $instance['title'] ) ? $instance['title'] : '';\n\t\t}\n\n\t\t/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */\n\t\t$widget_title = apply_filters( 'widget_title', empty( $widget_title ) ? '' : $widget_title, $instance, $this->id_base );\n\n\t\techo $args['before_widget'];\n\n\t\tif ( ! empty( $widget_title ) ) {\n\t\t\techo $args['before_title'] . $widget_title . $args['after_title'];\n\t\t}\n\n\t\t$nav_menu_args = array(\n\t\t\t'fallback_cb' => '',\n\t\t\t'menu'        => $nav_menu,\n\t\t);\n\n\t\t/**\n\t\t * Filters the arguments for the Associated Post Menu widget.\n\t\t *\n\t\t * @since 4.2.0\n\t\t * @since 4.4.0 Added the `$instance` parameter.\n\t\t *\n\t\t * @param array    $nav_menu_args {\n\t\t *     An array of arguments passed to wp_nav_menu() to retrieve a custom menu.\n\t\t *\n\t\t *     @type callable|bool $fallback_cb Callback to fire if the menu doesn't exist. Default empty.\n\t\t *     @type mixed         $menu        Menu ID, slug, or name.\n\t\t * }\n\t\t * @param WP_Term  $nav_menu      Nav menu object for the current menu.\n\t\t * @param array    $args          Display arguments for the current widget.\n\t\t * @param array    $instance      Array of settings for the current widget.\n\t\t */\n\t\twp_nav_menu( apply_filters( 'widget_nav_menu_args', $nav_menu_args, $nav_menu, $args, $instance ) );\n\n\t\techo $args['after_widget'];\n\t}\n\n\t/**\n\t * Handles updating settings for the current Associated Post Menu widget instance.\n\t *\n\t * @since 3.0.0\n\t * @access public\n\t *\n\t * @param array $new_instance New settings for this instance as input by the user via\n\t *                            WP_Widget::form().\n\t * @param array $old_instance Old settings for this instance.\n\t * @return array Updated settings to save.\n\t */\n\tpublic function update( $new_instance, $old_instance ) {\n\t\t$instance = array();\n\t\tif ( ! empty( $new_instance['title'] ) ) {\n\t\t\t$instance['title'] = sanitize_text_field( $new_instance['title'] );\n\t\t}\n\n\t\treturn $instance;\n\t}\n\n\t/**\n\t * Outputs the settings form for the Associated Post Menu widget.\n\t *\n\t * @since 3.0.0\n\t * @access public\n\t *\n\t * @param array $instance Current settings.\n\t */\n\tpublic function form( $instance ) {\n\t\t$title = isset( $instance['title'] ) ? $instance['title'] : '';\n\n\t\t// If no menus exists, direct the user to go and create some.\n\t\t?>\n\t\t<div class=\"nav-menu-widget-form-controls\">\n\t\t\t<p>\n\t\t\t\t<label for=\"<?php echo $this->get_field_id( 'title' ); ?>\">Fallback Title:</label>\n\t\t\t\t<input type=\"text\" class=\"widefat\" id=\"<?php echo $this->get_field_id( 'title' ); ?>\" name=\"<?php echo $this->get_field_name( 'title' ); ?>\" value=\"<?php echo esc_attr( $title ); ?>\"/>\n\t\t\t</p>\n\t\t</div>\n\t\t<?php\n\t}\n}\n\nfunction cmb2_register_post_menu_widget() {\n\tregister_widget('CMB2_Post_Menu_Widget');\n}\nadd_action( 'widgets_init', 'cmb2_register_post_menu_widget' );\n"
  },
  {
    "path": "custom-field-types/autocomplete-field-type.php",
    "content": "<?php\n\n/*\n * Plugin Name: CMB2 Custom Field Type - Autocomplete\n * Description: Makes available an autocomplete custom field type.\n * Author: johnsonpaul1014\n * Version: 1.0.0\n */\n\n/**\n *\n * It is a little complex but is very flexible, and it is a great option when you have\n * way too many things to put in a select.\n *\n * It uses a hidden field for the CMB2 data, and puts the value from the input used for the\n * autocomplete in the hidden field on blur of the autocomplete field so it always gets populated.\n *\n * There are two types available: options pre-built and one that uses a remote source.\n * To use the first option, simply build it like a select with the standard CMB2 \"options\" argument.\n * For a remote source, you will use the \"source\" argument that corresponds to an AJAX function.\n * Then, you pass in a \"matching_function\" to look up the selected autocomplete value using the\n * CMB2 field value.\n *\n * It can also be used as a repeatable field if a \"repeatable_class\" argument is passed when\n * constructing the field. This argument is necessary to allow for mapping the autocomplete\n * jQuery UI calls to the appropriate field.\n *\n * The example fields in this plugin demonstrate all types: pre-built single field,\n * pre-built repeatable field, remote single field and remote repeatable field.\n *\n */\n\n/**\n * Define the metabox and field configurations.\n */\nfunction autocomplete_cmb2_meta_boxes() {\n\n\t// Start with an underscore to hide fields from custom fields list\n\t$prefix = '_autocomplete_cmb2_';\n\n\t$cmb = new_cmb2_box( array(\n\t\t'id' => 'autocomplete_test',\n\t\t'title' => __('Autocomplete Field Examples', 'autocomplete_cmb2'),\n\t\t'object_types' => array('post'),\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name' => __('Related Fruit', 'autocomplete_cmb2'),\n\t\t'desc' => __('Fruit that is related to this post', 'autocomplete_cmb2'),\n\t\t'id' => $prefix.'related_fruit',\n\t\t'type' => 'autocomplete',\n\t\t'options' => array(\n\t\t\tarray('value' => 1, 'name' => 'Apple'),\n\t\t\tarray('value' => 2, 'name' => 'Orange'),\n\t\t\tarray('value' => 3, 'name' => 'Grape')\n\t\t)\n\t) );\n\t$cmb->add_field( array(\n\t\t'name' => __('Related Fruits', 'autocomplete_cmb2'),\n\t\t'desc' => __('Repeatable related fruits', 'autocomplete_cmb2'),\n\t\t'id' => $prefix.'related_fruits',\n\t\t'type' => 'autocomplete',\n\t\t'repeatable' => true,\n\t\t'repeatable_class' => 'related-fruits',\n\t\t'options' => array(\n\t\t\tarray('value' => 1, 'name' => 'Apple'),\n\t\t\tarray('value' => 2, 'name' => 'Orange'),\n\t\t\tarray('value' => 3, 'name' => 'Grape')\n\t\t)\n\t) );\n\t$cmb->add_field( array(\n\t\t'name' => __('Related Post', 'autocomplete_cmb2'),\n\t\t'desc' => __('Post that is related to this one', 'autocomplete_cmb2'),\n\t\t'id' => $prefix.'related_post',\n\t\t'type' => 'autocomplete',\n\t\t'source' => 'get_post_options',\n\t\t'mapping_function' => 'autocomplete_cmb2_get_post_title_from_id'\n\t) );\n\t$cmb->add_field( array(\n\t\t'name' => __('Related Posts', 'autocomplete_cmb2'),\n\t\t'desc' => __('Posts that are related to this one', 'autocomplete_cmb2'),\n\t\t'id' => $prefix.'related_posts',\n\t\t'repeatable' => true,\n\t\t'type' => 'autocomplete',\n\t\t'source' => 'get_post_options',\n\t\t'repeatable_class' => 'related-posts',\n\t\t'mapping_function' => 'autocomplete_cmb2_get_post_title_from_id'\n\t) );\n}\n\n/**\n * Gets the post title from the ID for mapping purposes in autocompletes.\n *\n * @param int $id\n * @return string\n */\nfunction autocomplete_cmb2_get_post_title_from_id($id) {\n\tif (empty($id)) {\n\t\treturn '';\n\t}\n\n\t$post = get_post($id);\n\n\treturn $post->post_title;\n}\n\n/**\n * Renders the autocomplete type\n *\n * @param CMB2_Field $field_object\n * @param string $escaped_value The value of this field passed through the escaping filter. It defaults to sanitize_text_field.\n *                 If you need the unescaped value, you can access it via $field_type_object->value().\n * @param string $object_id The id of the object you are working with. Most commonly, the post id.\n * @param string $object_type The type of object you are working with. Most commonly, post (this applies to all post-types),\n *                but could also be comment, user or options-page.\n * @param CMB2_Object $field_type_object This is an instance of the CMB2 object and gives you access to all of the methods that CMB2 uses to build its field types.\n */\nfunction autocomplete_cmb2_render_autocomplete($field_object, $escaped_value, $object_id, $object_type, $field_type_object) {\n\n\t// Store the value in a hidden field.\n\techo $field_type_object->hidden();\n\n\tif (isset($field_object->args['repeatable_class'])) {\n\t\t$repeatable_class = $field_object->args['repeatable_class'];\n\t}\n\n\t$options = $field_object->options();\n\n\t// Set up the options or source PHP variables.\n\tif (empty($options)) {\n\t\t$source = $field_object->args['source'];\n\t\t$value = $field_object->args['mapping_function']($field_object->escaped_value);\n\t} else {\n\n\t\t// Set the value.\n\t\tif (empty($field_object->escaped_value)) {\n\t\t\t$value = '';\n\t\t} else {\n\t\t\tforeach ($options as $option) {\n\t\t\t\tif ($option['value'] == $field_object->escaped_value) {\n\t\t\t\t\t$value = $option['name'];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Set up the autocomplete field.  Replace the '_' with '-' to not interfere with the ID from CMB2.\n\t$id = str_replace('_', '-', $field_object->args['id']);\n\tif ( '_' !== substr( $field_object->args['id'], 0, 1 ) ) {\n\t\t$id = '-' . $field_object->args['id'];\n\t}\n\n\t// Don't use the ID on repeatable elements as it won't change; use the class instead.\n\techo '<input size=\"50\"'.(empty($repeatable_class) ? ' id=\"'.$id.'\"' : '') . ' value=\"'.htmlspecialchars($value).'\"'.\n\t\t(!empty($repeatable_class) ? ' class=\"'.$repeatable_class.'\"' : '').'/>';\n\n\tif (!$field_object->args['repeatable'] && isset($field_object->args['desc'])) {\n\t\techo '<p class=\"cmb2-metabox-description\">'.$field_object->args['desc'].'</p>';\n\t}\n\n\t// Now, set up the script.\n\t?>\n\t<script>\n\t\tjQuery(document).ready(function($) {\n\t\t\tvar options = [];\n\t\t\tvar nameToValue = [];\n\n\t\t\t<?php\n\n\t\t\tif (!empty($options)) {\n\t\t\t\tforeach ($options as $option) {\n\t\t\t\t\techo \"options.push('\".addcslashes($option['name'], \"'\").\"');\\r\\n\";\n\t\t\t\t\techo \"nameToValue['\".addcslashes($option['name'], \"'\").\"'] = '\".$option['value'].\"';\\r\\n\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!empty($repeatable_class)) { ?>\n\t\t\t$('.<?php echo $repeatable_class; ?>').each(function(i, el) {\n\t\t\t\tif (typeof $(this).data('ui-autocomplete') === 'undefined') {\n\t\t\t\t\t\t$(this).autocomplete({\n\t\t\t<?php } else { ?>\n\t\t\t$('#<?php echo $id; ?>').autocomplete({\n\t\t\t<?php } ?>\n\t\t\t\tsource: <?php if (empty($options)) { ?>\n\t\t\t\t\tfunction(request, response) {\n\t\t\t\t\t\t$.ajax(\n\t\t\t\t\t\t\t{url: ajaxurl,\n\t\t\t\t\t\t\t data: {\n\t\t\t\t\t\t\t\taction: '<?php echo $source; ?>',\n\t\t\t\t\t\t\t\tq: request.term\n\t\t\t\t\t\t\t },\n\t\t\t\t\t\t\t success: function(data) {\n\n\t\t\t\t\t\t\t\t// Set up options and name to value for this returned set.\n\t\t\t\t\t\t\t\tvar values = $.parseJSON(data);\n\t\t\t\t\t\t\t\toptions = [];\n\t\t\t\t\t\t\t\tnameToValue = [];\n\n\t\t\t\t\t\t\t\tfor (optionI in values) {\n\t\t\t\t\t\t\t\t\tvar option = values[optionI];\n\t\t\t\t\t\t\t\t\toptions.push(option.name);\n\t\t\t\t\t\t\t\t\tnameToValue[option.name] = option.value;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tresponse(options);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t });\n\t\t\t\t\t\t} <?php } else {\n\t\t\t\t\t\t\techo 'options';\n\t\t\t\t\t\t} ?>\n\t\t\t});\n\n\t\t\t// Also set up a blur function to update the ID.\n\t\t\t$(<?php echo empty($repeatable_class) ? \"'#\".$id.\"'\" : 'this'; ?>).blur(function(e) {\n\t\t\t\t$(this).prev('input').val(nameToValue[$(this).val()]);\n\t\t\t});\n\n\t\t\t<?php\n\n\t\t\tif (!empty($repeatable_class)) { ?>\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t<?php\n\t\t\t}\n\t\t\t?>\n\t\t});\n\t</script>\n\t<?php\n}\n\n/**\n * Gets post options using a post type\n *\n * @param mixed $post_type array or string of post type(s)\n * @param boolean $include_empty whether or not to include an empty value\n * @param string $like_test used to query for posts like a certain value\n * @return array\n */\nfunction autocomplete_cmb2_get_post_options_using_post_type($post_type, $include_empty = false, $like_test = '%%') {\n\n\t// Use a query instead of \"get_posts\" to save on a ton of memory.\n\tglobal $wpdb;\n\n\tif (is_array($post_type)) {\n\t\t$post_type_query = \"('\".implode(\"', '\", $post_type).\"')\";\n\t} else {\n\t\t$post_type_query = \"('\".$post_type.\"')\";\n\t}\n\n\t$posts = $wpdb->get_results($wpdb->prepare(\"\n\t\tSELECT ID, post_title\n    FROM $wpdb->posts\n    WHERE post_status = 'publish' AND post_type IN $post_type_query AND post_title LIKE %s\n    ORDER BY post_title ASC\n \t\", $like_test), OBJECT);\n\n\tif ($include_empty) {\n\t\t$post_options = array(array('name' => '--- Select ---', 'value' => ''));\n\t} else {\n\t\t$post_options = array();\n\t}\n\n\tforeach ($posts as $post) {\n\t\t$post_options[] = array(\n\t\t\t 'name' => $post->post_title,\n\t\t\t 'value' => $post->ID\n\t\t);\n\t}\n\n\treturn $post_options;\n}\n\n\n/**\n * Gets the jQuery autocomplete widget ready.\n */\nfunction autocomplete_cmb2_admin_enqueue_scripts() {\n\twp_enqueue_script('jquery-ui-autocomplete');\n}\n\n/**\n * Gets the post options in JSON format for the autocomplete\n */\nfunction autocomplete_cmb2_get_post_autocomplete_options() {\n\tdie(json_encode(autocomplete_cmb2_get_post_options_using_post_type('post', false, '%'.$_GET['q'].'%')));\n}\n\nadd_action('cmb2_render_autocomplete', 'autocomplete_cmb2_render_autocomplete', 10, 5);\nadd_action('admin_enqueue_scripts', 'autocomplete_cmb2_admin_enqueue_scripts');\nadd_action('wp_ajax_get_post_options', 'autocomplete_cmb2_get_post_autocomplete_options');\nadd_filter('cmb2_admin_init', 'autocomplete_cmb2_meta_boxes');\n"
  },
  {
    "path": "custom-field-types/button-field-type.php",
    "content": "<?php\n/*\n * Button field type\n * Screenshot: https://github.com/CMB2/CMB2/assets/1098900/c8e1aa00-1947-480d-8c30-bd58f5e9dcbc\n */\n\n/**\n * Example field registration:\n *\n * function yourprefix_register_demo_metabox() {\n *\n *    $cmb_demo = new_cmb2_box( array(\n *        'id'            => 'demo_metabox',\n *        'title'         => __( 'Test Metabox', 'yourprefix' ),\n *        'object_types'  => array( 'page', ),\n *    ) );\n *\n *    $cmb_demo->add_field( array(\n *        'type'     => 'button',\n *        'name'     => __( 'Button', 'yourprefix' ),\n *        'desc'     => __( 'Button description (optional)', 'yourprefix' ),\n *        'id'       => '_yourprefix_demo_button',\n *        'attributes' => array(\n *            'value' => 'Click Me',\n *            'onclick' => 'alert(\\'You clicked the button!\\');',\n *        ),\n *    ) );\n * }\n * add_action( 'cmb2_init', 'yourprefix_register_demo_metabox' );\n */\n\n// render button\nadd_action( 'cmb2_render_button', 'jt_cmb2__cmb_render_button', 10, 5 );\nfunction jt_cmb2__cmb_render_button( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {\n\techo $field_type_object->input( array(\n\t\t'class' => 'button',\n\t\t'type' => 'button',\n\t) );\n}\n"
  },
  {
    "path": "custom-field-types/dashicon-radio-field/dashicon-radio-field.php",
    "content": "<?php\n/*\nPlugin Name: CMB2 Dashicon Radio Field\nDescription: https://github.com/modemlooper/cmb2-dashicon-radio\nVersion: 1.0.0\nAuthor: modemlooper\nAuthor URI: http://twitter.com/modemlooper\nLicense: GPL-2.0+\n*/\n\n/**\n * Custom render for dashicon_radio field\n *\n * @param  object $field\n * @param  string $escaped_value\n * @param  string $object_id\n * @param  string $object_type\n * @param  object $field_type_object\n * @return void\n */\nfunction ml_cmb2_render_dashicon_radio_callback( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {\n\n\tadd_filter( 'cmb2_list_input_attributes', 'ml_cmb2_dashicon_radio_attributes', 10, 4 );\n\n\t$field->args['options'] = ml_cmb2_dashicons_return_array();\n\techo $field_type_object->radio();\n\tremove_filter( 'cmb2_list_input_attributes', 'ml_cmb2_dashicon_radio_attributes', 10, 4 );\n\n\tml_cmb2_dashicon_radio_css();\n}\nadd_action( 'cmb2_render_dashicon_radio', 'ml_cmb2_render_dashicon_radio_callback', 10, 5 );\n\n/**\n * Custom labels for dashicon_radio inputs\n *\n * @param  array $args\n * @param  array $defaults\n * @param  object $field\n * @param  object $cmb\n * @return array\n */\nfunction ml_cmb2_dashicon_radio_attributes( $args, $defaults, $field, $cmb ) {\n\tif ( $args['value'] ) {\n\t\t$args['label'] = '<span class=\"dashicons ' . $args['value'] . '\"></span> ' . $args['label'];\n\t}\n\n\treturn $args;\n}\n\n/**\n * Custom CMB2 css for dashicon_radio field\n *\n * @return void\n */\nfunction ml_cmb2_dashicon_radio_css() {\n\tstatic $added = false;\n\tif ( $added ) {\n\t\treturn;\n\t}\n\t$added = true;\n\t?>\n\t<style type=\"text/css\" media=\"screen\">\n\t\t.cmb-type-dashicon-radio .cmb-td {\n\t\t\theight: 200px;\n\t\t\toverflow: scroll;\n\t\t\tbackground-color: rgb(240, 240, 240);\n\t\t}\n\t\t.cmb-type-dashicon-radio .cmb2-radio-list {\n\t\t\tpadding: 10px;\n\n\t\t}\n\t\t.cmb-type-dashicon-radio li {\n\t\t\tdisplay: inline-block;\n\t\t\tmin-width: 222px;\n\t\t}\n\t</style>\n\t<?php\n}\n\n/**\n * Returns array of dashicon data\n *\n * @return array\n */\nfunction ml_cmb2_dashicons_return_array() {\n\n\t$icons = array(\n\t\t'dashicons-menu'                    => __( 'Menu', 'cmb2' ),\n\t\t'dashicons-dashboard'               => __( 'Dashboard', 'cmb2' ),\n\t\t'dashicons-admin-site'              => __( 'Admin Site', 'cmb2' ),\n\t\t'dashicons-admin-media'             => __( 'Admin Media', 'cmb2' ),\n\t\t'dashicons-admin-page'              => __( 'Admin Page', 'cmb2' ),\n\t\t'dashicons-admin-comments'          => __( 'Admin Comments', 'cmb2' ),\n\t\t'dashicons-admin-appearance'        => __( 'Admin Appearance', 'cmb2' ),\n\t\t'dashicons-admin-plugins'           => __( 'Admin Plugins', 'cmb2' ),\n\t\t'dashicons-admin-users'             => __( 'Admin Users', 'cmb2' ),\n\t\t'dashicons-admin-tools'             => __( 'Admin Tools', 'cmb2' ),\n\t\t'dashicons-admin-settings'          => __( 'Admin Settings', 'cmb2' ),\n\t\t'dashicons-admin-network'           => __( 'Admin Network', 'cmb2' ),\n\t\t'dashicons-admin-generic'           => __( 'Admin Generic', 'cmb2' ),\n\t\t'dashicons-admin-home'              => __( 'Admin Home', 'cmb2' ),\n\t\t'dashicons-admin-collapse'          => __( 'Admin Collapse', 'cmb2' ),\n\t\t'dashicons-admin-links'             => __( 'Admin Links', 'cmb2' ),\n\t\t'dashicons-admin-post'              => __( 'Admin Post', 'cmb2' ),\n\t\t'dashicons-format-standard'         => __( 'Admin Plugins', 'cmb2' ),\n\t\t'dashicons-format-image'            => __( 'Image Post Format', 'cmb2' ),\n\t\t'dashicons-format-gallery'          => __( 'Gallery Post Format', 'cmb2' ),\n\t\t'dashicons-format-audio'            => __( 'Audio Post Format', 'cmb2' ),\n\t\t'dashicons-format-video'            => __( 'Video Post Format', 'cmb2' ),\n\t\t'dashicons-format-links'            => __( 'Link Post Format', 'cmb2' ),\n\t\t'dashicons-format-chat'             => __( 'Chat Post Format', 'cmb2' ),\n\t\t'dashicons-format-status'           => __( 'Status Post Format', 'cmb2' ),\n\t\t'dashicons-format-aside'            => __( 'Aside Post Format', 'cmb2' ),\n\t\t'dashicons-format-quote'            => __( 'Quote Post Format', 'cmb2' ),\n\t\t'dashicons-welcome-write-blog'      => __( 'Welcome Write Blog', 'cmb2' ),\n\t\t'dashicons-welcome-edit-page'       => __( 'Welcome Edit Page', 'cmb2' ),\n\t\t'dashicons-welcome-add-page'        => __( 'Welcome Add Page', 'cmb2' ),\n\t\t'dashicons-welcome-view-site'       => __( 'Welcome View Site', 'cmb2' ),\n\t\t'dashicons-welcome-widgets-menus'   => __( 'Welcome Widget Menus', 'cmb2' ),\n\t\t'dashicons-welcome-comments'        => __( 'Welcome Comments', 'cmb2' ),\n\t\t'dashicons-welcome-learn-more'      => __( 'Welcome Learn More', 'cmb2' ),\n\t\t'dashicons-image-crop'              => __( 'Image Crop', 'cmb2' ),\n\t\t'dashicons-image-rotate-left'       => __( 'Image Rotate Left', 'cmb2' ),\n\t\t'dashicons-image-rotate-right'      => __( 'Image Rotate Right', 'cmb2' ),\n\t\t'dashicons-image-flip-vertical'     => __( 'Image Flip Vertical', 'cmb2' ),\n\t\t'dashicons-image-flip-horizontal'   => __( 'Image Flip Horizontal', 'cmb2' ),\n\t\t'dashicons-undo'                    => __( 'Undo', 'cmb2' ),\n\t\t'dashicons-redo'                    => __( 'Redo', 'cmb2' ),\n\t\t'dashicons-editor-bold'             => __( 'Editor Bold', 'cmb2' ),\n\t\t'dashicons-editor-italic'           => __( 'Editor Italic', 'cmb2' ),\n\t\t'dashicons-editor-ul'               => __( 'Editor UL', 'cmb2' ),\n\t\t'dashicons-editor-ol'               => __( 'Editor OL', 'cmb2' ),\n\t\t'dashicons-editor-quote'            => __( 'Editor Quote', 'cmb2' ),\n\t\t'dashicons-editor-alignleft'        => __( 'Editor Align Left', 'cmb2' ),\n\t\t'dashicons-editor-aligncenter'      => __( 'Editor Align Center', 'cmb2' ),\n\t\t'dashicons-editor-alignright'       => __( 'Editor Align Right', 'cmb2' ),\n\t\t'dashicons-editor-insertmore'       => __( 'Editor Insert More', 'cmb2' ),\n\t\t'dashicons-editor-spellcheck'       => __( 'Editor Spell Check', 'cmb2' ),\n\t\t'dashicons-editor-distractionfree'  => __( 'Editor Distraction Free', 'cmb2' ),\n\t\t'dashicons-editor-expand'           => __( 'Editor Expand', 'cmb2' ),\n\t\t'dashicons-editor-contract'         => __( 'Editor Contract', 'cmb2' ),\n\t\t'dashicons-editor-kitchensink'      => __( 'Editor Kitchen Sink', 'cmb2' ),\n\t\t'dashicons-editor-underline'        => __( 'Editor Underline', 'cmb2' ),\n\t\t'dashicons-editor-justify'          => __( 'Editor Justify', 'cmb2' ),\n\t\t'dashicons-editor-textcolor'        => __( 'Editor Text Colour', 'cmb2' ),\n\t\t'dashicons-editor-paste-word'       => __( 'Editor Paste Word', 'cmb2' ),\n\t\t'dashicons-editor-paste-text'       => __( 'Editor Paste Text', 'cmb2' ),\n\t\t'dashicons-editor-removeformatting' => __( 'Editor Remove Formatting', 'cmb2' ),\n\t\t'dashicons-editor-video'            => __( 'Editor Video', 'cmb2' ),\n\t\t'dashicons-editor-customchar'       => __( 'Editor Custom Character', 'cmb2' ),\n\t\t'dashicons-editor-outdent'          => __( 'Editor Outdent', 'cmb2' ),\n\t\t'dashicons-editor-indent'           => __( 'Editor Indent', 'cmb2' ),\n\t\t'dashicons-editor-help'             => __( 'Editor Help', 'cmb2' ),\n\t\t'dashicons-editor-strikethrough'    => __( 'Editor Strikethrough', 'cmb2' ),\n\t\t'dashicons-editor-unlink'           => __( 'Editor Unlink', 'cmb2' ),\n\t\t'dashicons-editor-rtl'              => __( 'Editor RTL', 'cmb2' ),\n\t\t'dashicons-editor-break'            => __( 'Editor Break', 'cmb2' ),\n\t\t'dashicons-editor-code'             => __( 'Editor Code', 'cmb2' ),\n\t\t'dashicons-editor-paragraph'        => __( 'Editor Paragraph', 'cmb2' ),\n\t\t'dashicons-align-left'              => __( 'Align Left', 'cmb2' ),\n\t\t'dashicons-align-right'             => __( 'Align Right', 'cmb2' ),\n\t\t'dashicons-align-center'            => __( 'Align Center', 'cmb2' ),\n\t\t'dashicons-align-none'              => __( 'Align None', 'cmb2' ),\n\t\t'dashicons-lock'                    => __( 'Lock', 'cmb2' ),\n\t\t'dashicons-calendar'                => __( 'Calendar', 'cmb2' ),\n\t\t'dashicons-visibility'              => __( 'Visibility', 'cmb2' ),\n\t\t'dashicons-post-status'             => __( 'Post Status', 'cmb2' ),\n\t\t'dashicons-edit'                    => __( 'Edit', 'cmb2' ),\n\t\t'dashicons-post-trash'              => __( 'Post Trash', 'cmb2' ),\n\t\t'dashicons-trash'                   => __( 'Trash', 'cmb2' ),\n\t\t'dashicons-external'                => __( 'External', 'cmb2' ),\n\t\t'dashicons-arrow-up'                => __( 'Arrow Up', 'cmb2' ),\n\t\t'dashicons-arrow-down'              => __( 'Arrow Down', 'cmb2' ),\n\t\t'dashicons-arrow-left'              => __( 'Arrow Left', 'cmb2' ),\n\t\t'dashicons-arrow-right'             => __( 'Arrow Right', 'cmb2' ),\n\t\t'dashicons-arrow-up-alt'            => __( 'Arrow Up (alt)', 'cmb2' ),\n\t\t'dashicons-arrow-down-alt'          => __( 'Arrow Down (alt)', 'cmb2' ),\n\t\t'dashicons-arrow-left-alt'          => __( 'Arrow Left (alt)', 'cmb2' ),\n\t\t'dashicons-arrow-right-alt'         => __( 'Arrow Right (alt)', 'cmb2' ),\n\t\t'dashicons-arrow-up-alt2'           => __( 'Arrow Up (alt 2)', 'cmb2' ),\n\t\t'dashicons-arrow-down-alt2'         => __( 'Arrow Down (alt 2)', 'cmb2' ),\n\t\t'dashicons-arrow-left-alt2'         => __( 'Arrow Left (alt 2)', 'cmb2' ),\n\t\t'dashicons-arrow-right-alt2'        => __( 'Arrow Right (alt 2)', 'cmb2' ),\n\t\t'dashicons-leftright'               => __( 'Arrow Left-Right', 'cmb2' ),\n\t\t'dashicons-sort'                    => __( 'Sort', 'cmb2' ),\n\t\t'dashicons-randomize'               => __( 'Randomise', 'cmb2' ),\n\t\t'dashicons-list-view'               => __( 'List View', 'cmb2' ),\n\t\t'dashicons-exerpt-view'             => __( 'Excerpt View', 'cmb2' ),\n\t\t'dashicons-hammer'                  => __( 'Hammer', 'cmb2' ),\n\t\t'dashicons-art'                     => __( 'Art', 'cmb2' ),\n\t\t'dashicons-migrate'                 => __( 'Migrate', 'cmb2' ),\n\t\t'dashicons-performance'             => __( 'Performance', 'cmb2' ),\n\t\t'dashicons-universal-access'        => __( 'Universal Access', 'cmb2' ),\n\t\t'dashicons-universal-access-alt'    => __( 'Universal Access (alt)', 'cmb2' ),\n\t\t'dashicons-tickets'                 => __( 'Tickets', 'cmb2' ),\n\t\t'dashicons-nametag'                 => __( 'Name Tag', 'cmb2' ),\n\t\t'dashicons-clipboard'               => __( 'Clipboard', 'cmb2' ),\n\t\t'dashicons-heart'                   => __( 'Heart', 'cmb2' ),\n\t\t'dashicons-megaphone'               => __( 'Megaphone', 'cmb2' ),\n\t\t'dashicons-schedule'                => __( 'Schedule', 'cmb2' ),\n\t\t'dashicons-wordpress'               => __( 'WordPress', 'cmb2' ),\n\t\t'dashicons-wordpress-alt'           => __( 'WordPress (alt)', 'cmb2' ),\n\t\t'dashicons-pressthis'               => __( 'Press This', 'cmb2' ),\n\t\t'dashicons-update'                  => __( 'Update', 'cmb2' ),\n\t\t'dashicons-screenoptions'           => __( 'Screen Options', 'cmb2' ),\n\t\t'dashicons-info'                    => __( 'Info', 'cmb2' ),\n\t\t'dashicons-cart'                    => __( 'Cart', 'cmb2' ),\n\t\t'dashicons-feedback'                => __( 'Feedback', 'cmb2' ),\n\t\t'dashicons-cloud'                   => __( 'Cloud', 'cmb2' ),\n\t\t'dashicons-translation'             => __( 'Translation', 'cmb2' ),\n\t\t'dashicons-tag'                     => __( 'Tag', 'cmb2' ),\n\t\t'dashicons-category'                => __( 'Category', 'cmb2' ),\n\t\t'dashicons-archive'                 => __( 'Archive', 'cmb2' ),\n\t\t'dashicons-tagcloud'                => __( 'Tag Cloud', 'cmb2' ),\n\t\t'dashicons-text'                    => __( 'Text', 'cmb2' ),\n\t\t'dashicons-media-archive'           => __( 'Media Archive', 'cmb2' ),\n\t\t'dashicons-media-audio'             => __( 'Media Audio', 'cmb2' ),\n\t\t'dashicons-media-code'              => __( 'Media Code)', 'cmb2' ),\n\t\t'dashicons-media-default'           => __( 'Media Default', 'cmb2' ),\n\t\t'dashicons-media-document'          => __( 'Media Document', 'cmb2' ),\n\t\t'dashicons-media-interactive'       => __( 'Media Interactive', 'cmb2' ),\n\t\t'dashicons-media-spreadsheet'       => __( 'Media Spreadsheet', 'cmb2' ),\n\t\t'dashicons-media-text'              => __( 'Media Text', 'cmb2' ),\n\t\t'dashicons-media-video'             => __( 'Media Video', 'cmb2' ),\n\t\t'dashicons-playlist-audio'          => __( 'Audio Playlist', 'cmb2' ),\n\t\t'dashicons-playlist-video'          => __( 'Video Playlist', 'cmb2' ),\n\t\t'dashicons-yes'                     => __( 'Yes', 'cmb2' ),\n\t\t'dashicons-no'                      => __( 'No', 'cmb2' ),\n\t\t'dashicons-no-alt'                  => __( 'No (alt)', 'cmb2' ),\n\t\t'dashicons-plus'                    => __( 'Plus', 'cmb2' ),\n\t\t'dashicons-plus-alt'                => __( 'Plus (alt)', 'cmb2' ),\n\t\t'dashicons-minus'                   => __( 'Minus', 'cmb2' ),\n\t\t'dashicons-dismiss'                 => __( 'Dismiss', 'cmb2' ),\n\t\t'dashicons-marker'                  => __( 'Marker', 'cmb2' ),\n\t\t'dashicons-star-filled'             => __( 'Star Filled', 'cmb2' ),\n\t\t'dashicons-star-half'               => __( 'Star Half', 'cmb2' ),\n\t\t'dashicons-star-empty'              => __( 'Star Empty', 'cmb2' ),\n\t\t'dashicons-flag'                    => __( 'Flag', 'cmb2' ),\n\t\t'dashicons-share'                   => __( 'Share', 'cmb2' ),\n\t\t'dashicons-share1'                  => __( 'Share 1', 'cmb2' ),\n\t\t'dashicons-share-alt'               => __( 'Share (alt)', 'cmb2' ),\n\t\t'dashicons-share-alt2'              => __( 'Share (alt 2)', 'cmb2' ),\n\t\t'dashicons-twitter'                 => __( 'twitter', 'cmb2' ),\n\t\t'dashicons-rss'                     => __( 'RSS', 'cmb2' ),\n\t\t'dashicons-email'                   => __( 'Email', 'cmb2' ),\n\t\t'dashicons-email-alt'               => __( 'Email (alt)', 'cmb2' ),\n\t\t'dashicons-facebook'                => __( 'Facebook', 'cmb2' ),\n\t\t'dashicons-facebook-alt'            => __( 'Facebook (alt)', 'cmb2' ),\n\t\t'dashicons-networking'              => __( 'Networking', 'cmb2' ),\n\t\t'dashicons-googleplus'              => __( 'Google+', 'cmb2' ),\n\t\t'dashicons-location'                => __( 'Location', 'cmb2' ),\n\t\t'dashicons-location-alt'            => __( 'Location (alt)', 'cmb2' ),\n\t\t'dashicons-camera'                  => __( 'Camera', 'cmb2' ),\n\t\t'dashicons-images-alt'              => __( 'Images', 'cmb2' ),\n\t\t'dashicons-images-alt2'             => __( 'Images Alt', 'cmb2' ),\n\t\t'dashicons-video-alt'               => __( 'Video (alt)', 'cmb2' ),\n\t\t'dashicons-video-alt2'              => __( 'Video (alt 2)', 'cmb2' ),\n\t\t'dashicons-video-alt3'              => __( 'Video (alt 3)', 'cmb2' ),\n\t\t'dashicons-vault'                   => __( 'Vault', 'cmb2' ),\n\t\t'dashicons-shield'                  => __( 'Shield', 'cmb2' ),\n\t\t'dashicons-shield-alt'              => __( 'Shield (alt)', 'cmb2' ),\n\t\t'dashicons-sos'                     => __( 'SOS', 'cmb2' ),\n\t\t'dashicons-search'                  => __( 'Search', 'cmb2' ),\n\t\t'dashicons-slides'                  => __( 'Slides', 'cmb2' ),\n\t\t'dashicons-analytics'               => __( 'Analytics', 'cmb2' ),\n\t\t'dashicons-chart-pie'               => __( 'Pie Chart', 'cmb2' ),\n\t\t'dashicons-chart-bar'               => __( 'Bar Chart', 'cmb2' ),\n\t\t'dashicons-chart-line'              => __( 'Line Chart', 'cmb2' ),\n\t\t'dashicons-chart-area'              => __( 'Area Chart', 'cmb2' ),\n\t\t'dashicons-groups'                  => __( 'Groups', 'cmb2' ),\n\t\t'dashicons-businessman'             => __( 'Businessman', 'cmb2' ),\n\t\t'dashicons-id'                      => __( 'ID', 'cmb2' ),\n\t\t'dashicons-id-alt'                  => __( 'ID (alt)', 'cmb2' ),\n\t\t'dashicons-products'                => __( 'Products', 'cmb2' ),\n\t\t'dashicons-awards'                  => __( 'Awards', 'cmb2' ),\n\t\t'dashicons-forms'                   => __( 'Forms', 'cmb2' ),\n\t\t'dashicons-testimonial'             => __( 'Testimonial', 'cmb2' ),\n\t\t'dashicons-portfolio'               => __( 'Portfolio', 'cmb2' ),\n\t\t'dashicons-book'                    => __( 'Book', 'cmb2' ),\n\t\t'dashicons-book-alt'                => __( 'Book (alt)', 'cmb2' ),\n\t\t'dashicons-download'                => __( 'Download', 'cmb2' ),\n\t\t'dashicons-upload'                  => __( 'Upload', 'cmb2' ),\n\t\t'dashicons-backup'                  => __( 'Backup', 'cmb2' ),\n\t\t'dashicons-clock'                   => __( 'Clock', 'cmb2' ),\n\t\t'dashicons-lightbulb'               => __( 'Lightbulb', 'cmb2' ),\n\t\t'dashicons-microphone'              => __( 'Microphone', 'cmb2' ),\n\t\t'dashicons-desktop'                 => __( 'Desktop', 'cmb2' ),\n\t\t'dashicons-tablet'                  => __( 'Tablet', 'cmb2' ),\n\t\t'dashicons-smartphone'              => __( 'Smartphone', 'cmb2' ),\n\t\t'dashicons-smiley'                  => __( 'Smiley', 'cmb2' ),\n\t);\n\n\treturn $icons;\n}\n"
  },
  {
    "path": "custom-field-types/default-category-field.php",
    "content": "<?php\n/**\n * CMB2 Default Category field/metabox\n *\n * @package CMB2 Default Category field/metabox\n */\n\n/**\n * Adds a custom field type which uses the WordPress default post-categories metabox.\n *\n * @param  object $field             The CMB2_Field type object.\n * @param  string $value             The saved (and escaped) value.\n * @param  int    $object_id         The current post ID.\n * @param  string $object_type       The current object type.\n * @return void\n */\nfunction cmb2_render_default_categories_field_type( $field, $escaped_value, $object_id, $object_type ) {\n\n\t$taxonomy = $field->args( 'taxonomy' );\n\t$taxonomy = $taxonomy ? $taxonomy : 'category';\n\n\tif ( 'post' !== $object_type ) {\n\t\twp_die( 'This won\\'t work for non-\"post\" object types!' );\n\t}\n\n\tremove_meta_box( \"{$object_type}div\", 'post', 'side' );\n\n\tpost_categories_meta_box( get_post( $object_id ), array(\n\t\t'args' => array(\n\t\t\t'taxonomy' => $taxonomy,\n\t\t),\n\t) );\n}\nadd_action( 'cmb2_render_default_categories', 'cmb2_render_default_categories_field_type', 10, 4 );\n\nfunction cmb2_default_categories_let_wp_save( $null, $a, $field_args, $field ) {\n\tif ( 'default_categories' === $field->args( 'type' ) ) {\n\t\t// Let WP handle it.\n\t\treturn false;\n\t}\n\n\treturn $null;\n}\nadd_filter( 'cmb2_override_meta_save', 'cmb2_default_categories_let_wp_save', 10, 4 );\n\nfunction cmb2_remove_default_category_metabox_for_taxonomy() {\n\tforeach ( CMB2_Boxes::get_all() as $cmb ) {\n\t\tforeach ( $cmb->prop( 'fields' ) as $field ) {\n\t\t\tif ( 'default_categories' === $field['type'] ) {\n\t\t\t\t$taxonomy = isset( $field['taxonomy'] ) ? $field['taxonomy'] : 'category';\n\t\t\t\tremove_meta_box( \"{$taxonomy}div\", 'post', 'side' );\n\t\t\t}\n\t\t}\n\t}\n}\nadd_action( 'admin_init', 'cmb2_remove_default_category_metabox_for_taxonomy' );\n"
  },
  {
    "path": "custom-field-types/default-tags-field.php",
    "content": "<?php\n/**\n * CMB2 Default Tags field/metabox\n *\n * @package CMB2 Default Tags field/metabox\n */\n\n/**\n * Adds a custom field type which uses the WordPress default post-tags metabox.\n *\n * @param  object $field             The CMB2_Field type object.\n * @param  string $value             The saved (and escaped) value.\n * @param  int    $object_id         The current post ID.\n * @param  string $object_type       The current object type.\n * @return void\n */\nfunction cmb2_render_default_tags_field_type( $field, $escaped_value, $object_id, $object_type ) {\n\n\t$taxonomy = $field->args( 'taxonomy' );\n\t$taxonomy = $taxonomy ? $taxonomy : 'post_tag';\n\n\tif ( 'post' !== $object_type ) {\n\t\twp_die( 'This won\\'t work for non-\"post\" object types!' );\n\t}\n\n\twp_enqueue_script( 'tags-box' );\n\tadd_action( 'admin_footer', 'cmb2_init_post_tag_box' );\n\n\tpost_tags_meta_box( get_post( $object_id ), array(\n\t\t'args' => array(\n\t\t\t'taxonomy' => $taxonomy,\n\t\t),\n\t) );\n}\nadd_action( 'cmb2_render_default_tags', 'cmb2_render_default_tags_field_type', 10, 4 );\n\nfunction cmb2_init_post_tag_box() {\n\t?>\n\t<script type=\"text/javascript\">\n\tjQuery( function( $ ) {\n\t\twindow.tagBox && window.tagBox.init();\n\t});\n\t</script>\n\t<?php\n}\n\nfunction cmb2_default_tags_let_wp_save( $null, $a, $field_args, $field ) {\n\tif ( 'default_tags' === $field->args( 'type' ) ) {\n\t\t// Let WP handle it.\n\t\treturn false;\n\t}\n\n\treturn $null;\n}\nadd_filter( 'cmb2_override_meta_save', 'cmb2_default_tags_let_wp_save', 10, 4 );\n\nfunction cmb2_remove_default_tag_metabox_for_taxonomy() {\n\tforeach ( CMB2_Boxes::get_all() as $cmb ) {\n\t\tforeach ( $cmb->prop( 'fields' ) as $field ) {\n\t\t\tif ( 'default_tags' === $field['type'] ) {\n\t\t\t\t$taxonomy = isset( $field['taxonomy'] ) ? $field['taxonomy'] : 'post_tag';\n\t\t\t\tremove_meta_box( \"tagsdiv-{$taxonomy}\", 'post', 'side' );\n\t\t\t}\n\t\t}\n\t}\n}\nadd_action( 'admin_init', 'cmb2_remove_default_tag_metabox_for_taxonomy' );\n\n"
  },
  {
    "path": "custom-field-types/form-field-field-type.php",
    "content": "<?php\n/*\n * Plugin Name: CMB2 Custom Field Type - Form Fields\n * Description: Makes available a 'formfield' CMB2 Custom Field Type. Based on https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-field-types#example-4-multiple-inputs-one-field-lets-create-an-formfield-field\n * Author: jtsternberg\n * Author URI: http://dsgnwrks.pro\n * Version: 0.1.0\n *\n * Example use with translation of strings:\n *\n * $cmb_demo->add_field( array(\n *     'name'       => 'Campos',\n *     'desc'       => 'Adiciona campos ao formulário',\n *     'id'         => '_form_fields',\n *     'type'       => 'formfield',\n *     'repeatable' => true,\n *     'text'       => array(\n *         'add_row_text'                       => 'Adicionar Campo',\n *         'formfield_field_id_label'           => 'ID do Campo',\n *         'formfield_field_label_label'        => 'Título do campo',\n *         'formfield_field_type_label'         => 'Tipo do campo',\n *         'formfield_field_size_label'         => 'Tamanho do campo',\n *         'formfield_text_field_option_label'  => 'Texto',\n *         'formfield_email_field_option_label' => 'Email',\n *         'formfield_money_field_option_label' => 'Dinheiro',\n *         'formfield_date_field_option_label'  => 'Data',\n *     ),\n * ) );\n *\n */\n\n\n/**\n * Render 'formfield' custom field type\n *\n * @since 0.1.0\n *\n * @param array  $field       The passed in `CMB2_Field` object\n * @param mixed  $value       The value of this field escaped.\n *                            It defaults to `sanitize_text_field`.\n *                            If you need the unescaped value, you can access it\n *                            via `$field->value()`\n * @param int    $object_id   The ID of the current object\n * @param string $object_type The type of object you are working with.\n *                            Most commonly, `post` (this applies to all post-types),\n *                            but could also be `comment`, `user` or `options-page`.\n * @param object $field_type  The `CMB2_Types` object\n */\nfunction jt_cmb2_render_formfield_field_callback( $field, $value, $object_id, $object_type, $field_type ) {\n\n\t// make sure we specify each part of the value we need.\n\t$value = wp_parse_args( $value, array(\n\t\t'id'    => '',\n\t\t'label' => '',\n\t\t'type'  => 'text',\n\t\t'size'  => '',\n\t) );\n\n\t$type_options = array(\n\t\t'text'  => $field_type->_text( 'formfield_text_field_option_label', 'Text' ),\n\t\t'email' => $field_type->_text( 'formfield_email_field_option_label', 'Email' ),\n\t\t'money' => $field_type->_text( 'formfield_money_field_option_label', 'Money' ),\n\t\t'date'  => $field_type->_text( 'formfield_date_field_option_label', 'Date' ),\n\t);\n\n\t$types = '';\n\tforeach ( $type_options as $type => $label ) {\n\t\t$selected = selected( $value['type'], $type, false );\n\t\t$label    = esc_html( $label );\n\t\t$types    .= \"<option value=\\\"{$type}\\\" {$selected}>{$label}</option>\";\n\t}\n\n\t?>\n\t<table>\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t<label for=\"<?php echo $field_type->_id( '_formfield_1' ); ?>\"><?php echo esc_html( $field_type->_text( 'formfield_field_id_label', 'Field ID' ) ); ?></label>\n\t\t\t\t<?php\n\t\t\t\techo $field_type->input( array(\n\t\t\t\t\t'name'  => $field_type->_name( '[id]' ),\n\t\t\t\t\t'id'    => $field_type->_id( '_id' ),\n\t\t\t\t\t'value' => $value['id'],\n\t\t\t\t\t'desc'  => ''\n\t\t\t\t) )\n\t\t\t\t?>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t<label for=\"<?php echo $field_type->_id( '_formfield_1' ); ?>\"><?php echo esc_html( $field_type->_text( 'formfield_field_label_label', 'Field label' ) ); ?></label>\n\t\t\t\t<?php\n\t\t\t\techo $field_type->input( array(\n\t\t\t\t\t'name'  => $field_type->_name( '[label]' ),\n\t\t\t\t\t'id'    => $field_type->_id( '_label' ),\n\t\t\t\t\t'value' => $value['label'],\n\t\t\t\t\t'desc'  => ''\n\t\t\t\t) )\n\t\t\t\t?>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t<label for=\"<?php echo $field_type->_id( '_formfield_1' ); ?>\"><?php echo esc_html( $field_type->_text( 'formfield_field_type_label', 'Field type' ) ); ?></label>\n\t\t\t\t<?php\n\t\t\t\techo $field_type->select( array(\n\t\t\t\t\t'name'    => $field_type->_name( '[type]' ),\n\t\t\t\t\t'id'      => $field_type->_id( '_type' ),\n\t\t\t\t\t'options' => $types,\n\t\t\t\t\t'desc' => ''\n\t\t\t\t) )\n\t\t\t\t?>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t<label for=\"<?php echo $field_type->_id( '_formfield_1' ); ?>\"><?php echo esc_html( $field_type->_text( 'formfield_field_size_label', 'Field Size' ) ); ?></label>\n\t\t\t\t<?php\n\t\t\t\techo $field_type->input( array(\n\t\t\t\t\t'name'  => $field_type->_name( '[size]' ),\n\t\t\t\t\t'id'    => $field_type->_id( '_size' ),\n\t\t\t\t\t'value' => $value['size'],\n\t\t\t\t\t'desc'  => ''\n\t\t\t\t) )\n\t\t\t\t?>\n\t\t\t</td>\n\t\t</tr>\n\t</table>\n\t<?php if ( $field_type->_desc() ) : ?>\n\t\t<p class=\"clear\">\n\t\t\t<?php echo $field_type->_desc();?>\n\t\t</p>\n\t<?php endif;\n}\nadd_filter( 'cmb2_render_formfield', 'jt_cmb2_render_formfield_field_callback', 10, 5 );\n\n/**\n * The following snippets are required for allowing the formfield field\n * to work as a repeatable field, or in a repeatable group\n */\nfunction jt_cmb2_sanitize_formfield_field( $check, $meta_value, $object_id, $field_args ) {\n\n\t// Nothing needed if not array value or not a repeatable field.\n\tif ( ! is_array( $meta_value ) || empty( $field_args['repeatable'] ) ) {\n\t\treturn $check;\n\t}\n\n\tforeach ( $meta_value as $key => $val ) {\n\t\t$val['type'] = isset( $val['type'] ) ? $val['type'] : 'text';\n\t\tif ( 'text' === $val['type'] ) {\n\t\t\tunset( $val['type'] );\n\t\t\t$val = array_filter( $val );\n\t\t\tif ( empty( $val ) ) {\n\t\t\t\tunset( $meta_value[ $key ] );\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\t$val['type'] = 'text';\n\t\t\t}\n\t\t}\n\t\t$meta_value[ $key ] = array_map( 'sanitize_text_field', $val );\n\t}\n\n\treturn $meta_value;\n}\nadd_filter( 'cmb2_sanitize_formfield', 'jt_cmb2_sanitize_formfield_field', 10, 4 );\n\nfunction jt_cmb2_types_esc_formfield_field( $check, $meta_value, $field_args ) {\n\n\t// Nothing needed if not array value or not a repeatable field.\n\tif ( ! is_array( $meta_value ) || empty( $field_args['repeatable'] ) ) {\n\t\treturn $check;\n\t}\n\n\tforeach ( $meta_value as $key => $val ) {\n\t\t$meta_value[ $key ] = array_map( 'esc_attr', $val );\n\t}\n\n\treturn $meta_value;\n}\nadd_filter( 'cmb2_types_esc_formfield', 'jt_cmb2_types_esc_formfield_field', 10, 3 );"
  },
  {
    "path": "custom-field-types/multicheck_posttype-field_type.php",
    "content": "<?php\n/**\n * CMB2 Multicheck by Post Type\n *\n * @package CMB2 Default Tags field/metabox\n * @author Daniele Mte90 Scasciafratte\n */\n\nadd_action( 'cmb2_render_multicheck_posttype', 'ds_cmb_render_multicheck_posttype', 10, 5 );\n\nfunction ds_cmb_render_multicheck_posttype( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {\n\n\tif ( version_compare( CMB2_VERSION, '2.2.2', '>=' ) ) {\n\t\t$field_type_object->type = new CMB2_Type_Radio( $field_type_object );\n\t}\n\n\t$cpts = get_post_types();\n\t// To disable the avalaibility of post types\n\tunset( $cpts[ 'nav_menu_item' ] );\n\tunset( $cpts[ 'revision' ] );\n\t$cpts = apply_filters( 'multicheck_posttype_' . $field->args[ '_id' ], $cpts );\n\t$options = '';\n\t$i = 1;\n\t$values = (array) $escaped_value;\n\n\tif ( $cpts ) {\n\t\tforeach ( $cpts as $cpt ) {\n\t\t\t$args = array(\n\t\t\t    'value' => $cpt,\n\t\t\t    'label' => $cpt,\n\t\t\t    'type' => 'checkbox',\n\t\t\t    'name' => $field->args['_name'] . '[]',\n\t\t\t);\n\n\t\t\tif ( in_array( $cpt, $values ) ) {\n\t\t\t\t$args[ 'checked' ] = 'checked';\n\t\t\t}\n\t\t\t$options .= $field_type_object->list_input( $args, $i );\n\t\t\t$i++;\n\t\t}\n\t}\n\n\t$classes = false === $field->args( 'select_all_button' ) ? 'cmb2-checkbox-list no-select-all cmb2-list' : 'cmb2-checkbox-list cmb2-list';\n\techo $field_type_object->radio( array( 'class' => $classes, 'options' => $options ), 'multicheck_posttype' );\n}\n\n"
  },
  {
    "path": "custom-field-types/multicheck_title-field_type.php",
    "content": "<?php\n\n/**\n * CMB2 Multicheck by Title\n *\n * @package CMB2 Default Tags field/metabox\n * @author Daniele Mte90 Scasciafratte\n */\n \n/*\n// Example\n$array[ 'id_of_the_key' ] = __( 'ID of the key' );\n$fields[ 'Title of the section' ] = $array;\n$cmb->add_field( array(\n\t'name' => __( 'Fields Extra', $this->plugin_slug ),\n\t'id' => 'extra_fields',\n\t'type' => 'multicheck_title',\n\t'data' => $fields\n) );\n*/\n\n// render Title multicheck\nadd_action( 'cmb2_render_multicheck_title', 'cmb_render_multicheck_title', 10, 5 );\n\nfunction cmb_render_multicheck_title( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {\n\t$data_field = $field->args[ 'data' ];\n\t$values = ( array ) $escaped_value;\n\t$i = 0;\n\n\tif ( version_compare( CMB2_VERSION, '2.2.2', '>=' ) ) {\n\t\t$field_type_object->type = new CMB2_Type_Multicheck( $field_type_object );\n\t}\n\n\tif ( $data_field ) {\n\t\tforeach ( $data_field as $title => $extra_fields ) {\n\t\t\t$options = '';\n\t\t\tforeach ( $extra_fields as $extra_field => $value ) {\n\t\t\t\t$args = array(\n\t\t\t\t    'value' => $extra_field,\n\t\t\t\t    'label' => $value,\n\t\t\t\t    'type' => 'checkbox',\n\t\t\t\t    'name' => $field->args[ '_name' ] . '[]',\n\t\t\t\t);\n\n\t\t\t\tif ( in_array( $extra_field, $values) ) {\n\t\t\t\t\t$args[ 'checked' ] = 'checked';\n\t\t\t\t}\n\t\t\t\t$options .= $field_type_object->list_input( $args, $i );\n\t\t\t\t$i++;\n\t\t\t}\n\t\t\techo '<h2>'.$title.'</h2>';\n\t\t\t$classes = false === $field->args( 'select_all_button' ) ? 'cmb2-checkbox-list no-select-all cmb2-list' : 'cmb2-checkbox-list cmb2-list';\n\t\t\techo $field_type_object->radio( array( 'class' => $classes, 'options' => $options ), 'title_multicheck' );\n\t\t}\n\t} else {\n\t\techo __( 'Nothing' );\n\t}\n}\n"
  },
  {
    "path": "custom-field-types/post-list-field-type.php",
    "content": "<?php\n//By Daniele Mte90 Scasciafratte\n//Based on https://github.com/WebDevStudios/CMB2-Post-Search-field\n//That field have a list separated by a comma of post id and allow to sort and remove\n//render post_list\n\nfunction cmb2_post_list_render_field( $field, $escaped_value, $object_id, $object_type, $field_type ) {\n\t$select_type = $field->args( 'select_type' );\n\n\techo $field_type->input( array(\n\t    'autocomplete' => 'off',\n\t    'style' => 'display:none'\n\t) );\n\techo '<ul style=\"cursor:move\">';\n\tif ( !empty( $field->escaped_value ) ) {\n\t\t$list = explode( ',', $field->escaped_value );\n\t\tforeach ( $list as $value ) {\n\t\t\techo '<li data-id=\"' . trim( $value ) . '\"><b>' . __( 'Title' ) . ':</b> ' . get_the_title( $value );\n\t\t\techo '<div title=\"' . __( 'Remove' ) . '\" style=\"color: #999;margin: -0.1em 0 0 2px; cursor: pointer;\" class=\"cmb-post-list-remove dashicons dashicons-no\"></div>';\n\t\t\techo '</li>';\n\t\t}\n\t}\n\techo '</ul>';\n\n\t// JS needed for modal\n\twp_enqueue_script( 'jquery' );\n\twp_enqueue_script( 'jquery-ui-sortable' );\n\n\tif ( !is_admin() ) {\n\t\t// Will need custom styling!\n\t\t// @todo add styles for front-end\n\t\trequire_once( ABSPATH . 'wp-admin/includes/template.php' );\n\t}\n\n\t?>\n\t<script type=\"text/javascript\">\n\t\tjQuery(document).ready(function ($) {\n\t\t  'use strict';\n\n\t\t  $('.cmb-type-post-list').on('click', '.cmb-post-list-remove', function () {\n\t\t    var ids = $('.cmb-type-post-list.cmb2-id-<?php echo str_replace( '_', '-', sanitize_html_class( $field->args( 'id' ) ) ) ?>').find('.cmb-td input[type=\"text\"]').val();\n\t\t    var $choosen = $(this);\n\t\t    if (ids.indexOf(',') !== -1) {\n\t\t      ids = ids.split(',');\n\t\t      var loopids = ids.slice(0);\n\t\t      $.each(loopids, function (index, value) {\n\t\t\tvar cleaned = value.trim().toString();\n\t\t\tif (String($choosen.parent().data('id')) === cleaned) {\n\t\t\t  $choosen.parent().remove();\n\t\t\t  ids.splice(index, 1);\n\t\t\t}\n\t\t      });\n\t\t      $('.cmb-type-post-list.cmb2-id-<?php echo str_replace( '_', '-', sanitize_html_class( $field->args( 'id' ) ) ) ?>').find('.cmb-td input[type=\"text\"]').val(ids.join(','));\n\t\t    } else {\n\t\t      $choosen.parent().remove();\n\t\t      $('.cmb-type-post-list.cmb2-id-<?php echo str_replace( '_', '-', sanitize_html_class( $field->args( 'id' ) ) ) ?>').find('.cmb-td input[type=\"text\"]').val('');\n\t\t    }\n\t\t  });\n\n\t\t  $(\".cmb-type-post-list.cmb2-id-<?php echo str_replace( '_', '-', sanitize_html_class( $field->args( 'id' ) ) ) ?> ul\").sortable({\n\t\t    update: function (event, ui) {\n\t\t      var ids = [];\n\t\t      $('.cmb-type-post-list.cmb2-id-<?php echo str_replace( '_', '-', sanitize_html_class( $field->args( 'id' ) ) ) ?> ul li').each(function (index, value) {\n\t\t\tids.push($(this).data('id'));\n\t\t      });\n\t\t      $('.cmb-type-post-list.cmb2-id-<?php echo str_replace( '_', '-', sanitize_html_class( $field->args( 'id' ) ) ) ?>').find('.cmb-td input[type=\"text\"]').val(ids.join(', '));\n\t\t    }\n\t\t  });\n\n\t\t});\n\t</script>\n\t<?php\n}\n\nadd_action( 'cmb2_render_post_list', 'cmb2_post_list_render_field', 10, 5 );\n\n"
  },
  {
    "path": "custom-field-types/select-multiple-field-type.php",
    "content": "<?php\n/**\n * CMB2 Select Multiple Custom Field Type\n * @package CMB2 Select Multiple Field Type\n */\n\n/**\n * Adds a custom field type for select multiples.\n * @param  object $field             The CMB2_Field type object.\n * @param  string $value             The saved (and escaped) value.\n * @param  int    $object_id         The current post ID.\n * @param  string $object_type       The current object type.\n * @param  object $field_type_object The CMB2_Types object.\n * @return void\n */\nfunction cmb2_render_select_multiple_field_type( $field, $escaped_value, $object_id, $object_type, $field_type_object ) {\n\n\t$select_multiple = '<select class=\"widefat\" multiple name=\"' . $field->args['_name'] . '[]\" id=\"' . $field->args['_id'] . '\"';\n\tforeach ( $field->args['attributes'] as $attribute => $value ) {\n\t\t$select_multiple .= \" $attribute=\\\"$value\\\"\";\n\t}\n\t$select_multiple .= ' />';\n\n\tforeach ( $field->options() as $value => $name ) {\n\t\t$selected = ( $escaped_value && in_array( $value, $escaped_value ) ) ? 'selected=\"selected\"' : '';\n\t\t$select_multiple .= '<option class=\"cmb2-option\" value=\"' . esc_attr( $value ) . '\" ' . $selected . '>' . esc_html( $name ) . '</option>';\n\t}\n\n\t$select_multiple .= '</select>';\n\t$select_multiple .= $field_type_object->_desc( true );\n\n\techo $select_multiple; // WPCS: XSS ok.\n}\nadd_action( 'cmb2_render_select_multiple', 'cmb2_render_select_multiple_field_type', 10, 5 );\n\n\n/**\n * Sanitize the selected value.\n */\nfunction cmb2_sanitize_select_multiple_callback( $override_value, $value ) {\n\tif ( is_array( $value ) ) {\n\t\tforeach ( $value as $key => $saved_value ) {\n\t\t\t$value[$key] = sanitize_text_field( $saved_value );\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\treturn;\n}\nadd_filter( 'cmb2_sanitize_select_multiple', 'cmb2_sanitize_select_multiple_callback', 10, 2 );\n\n"
  },
  {
    "path": "custom-field-types/star-rating-field-type/css/star-rating-field-type.css",
    "content": "﻿@charset \"UTF-8\";\n#cmb2-star-rating-metabox .star-cb-group {\n  /* remove inline-block whitespace */\n  font-size: 0;\n  /* flip the order so we can use the + and ~ combinators */\n  unicode-bidi: bidi-override;\n  direction: rtl;\n  /* the hidden clearer */\n}\n#cmb2-star-rating-metabox .star-cb-group * {\n  font-size: 1rem;\n}\n#cmb2-star-rating-metabox .star-cb-group > input {\n  display: none;\n}\n#cmb2-star-rating-metabox .star-cb-group > input + label {\n  /* only enough room for the star */\n  display: inline-block;\n  overflow: hidden;\n  text-indent: 9999px;\n  width: 1.75em;\n  white-space: nowrap;\n  cursor: pointer;\n}\n#cmb2-star-rating-metabox .star-cb-group > input + label:before {\n  display: inline-block;\n  text-indent: -9999px;\n  content: \"\\f154\";\n  font-family: 'dashicons';\n  color: #888;\n  font-size: 1.75em;\n}\n#cmb2-star-rating-metabox .star-cb-group > input:checked ~ label:before, \n#cmb2-star-rating-metabox  .star-cb-group > input + label:hover ~ label:before, \n#cmb2-star-rating-metabox .star-cb-group > input + label:hover:before {\n  content: \"\\f155\";\n  font-family: 'dashicons';\n  color: #FFC926;\n  text-shadow: 0 0 1px #333;\n  font-size: 1.75em;\n}\n#cmb2-star-rating-metabox .star-cb-group > .star-cb-clear + label {\n  text-indent: -9999px;\n  width: .5em;\n  margin-left: -.5em;\n}\n#cmb2-star-rating-metabox .star-cb-group > .star-cb-clear + label:before {\n  width: .5em;\n}\n#cmb2-star-rating-metabox .star-cb-group:hover > input + label:before {\n  content: \"\\f154\";\n  font-family: 'dashicons';\n  color: #888;\n  text-shadow: none;\n  font-size: 1.75em;\n}\n#cmb2-star-rating-metabox .star-cb-group:hover > input + label:hover ~ label:before, \n#cmb2-star-rating-metabox .star-cb-group:hover > input + label:hover:before {\n  content: \"\\f155\";\n  font-family: 'dashicons';\n  color: #FFC926;\n  text-shadow: 0 0 1px #333;\n  font-size: 1.75em;\n}\n#cmb2-star-rating-metabox fieldset {\n  border: 0;\n  background: transparent;\n  border-radius: 1px;\n}"
  },
  {
    "path": "custom-field-types/star-rating-field-type/star-rating-field-type.php",
    "content": "<?php\n/*\n * Plugin Name: CMB2 Custom Field Type - Star Rating\n * Description: Makes available a 'star_rating' CMB2 Custom Field Type. Based on https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-field-types#example-4-multiple-inputs-one-field-lets-create-an-address-field\n * Author: Evan Herman\n * Author URI: https://www.evan-herman.com\n * Version: 0.1.0\n */\n\n/**\n * Template tag for displaying an star rating from the CMB2 star rating field type (on the front-end)\n *\n * @since  0.1.0\n *\n * @param  string  $metakey The 'id' of the 'star rating' field (the metakey for get_post_meta)\n * @param  integer $post_id (optional) post ID. If using in the loop, it is not necessary\n */\nfunction eh_cmb2_star_rating_field( $metakey, $post_id = 0 ) {\n\techo eh_cmb2_get_star_rating_field( $metakey, $post_id );\n}\n\n/**\n * Template tag for returning a star rating from the CMB2 star rating field type (on the front-end)\n *\n * @since  0.1.0\n *\n * @param  string  $metakey The 'id' of the 'star rating' field (the metakey for get_post_meta)\n * @param  integer $post_id (optional) post ID. If using in the loop, it is not necessary\n */\nfunction eh_cmb2_get_star_rating_field( $metakey, $post_id = 0 ) {\n\t$post_id = $post_id ? $post_id : get_the_ID();\n\t$rating = get_post_meta( $post_id, $metakey, 1 );\n\t$stars_container = '<section class=\"cmb2-star-container\">';\n\t$x = 1;\n\t$total = 5;\n\t\twhile( $x <= $rating ) {\n\t\t\t$stars_container .= '<span class=\"dashicons dashicons-star-filled\"></span>';\n\t\t\t$x++;\n\t\t}\n\t\tif( $rating < $total ) {\n\t\t\twhile( $rating < $total ) {\n\t\t\t\t$stars_container .= '<span class=\"dashicons dashicons-star-empty\"></span>';\n\t\t\t\t$rating++;\n\t\t\t}\n\t\t}\n\t$stars_container .= '</section>';\n\twp_enqueue_style( 'dashicons' );\n\treturn $stars_container;\n}\n\n/**\n * Render 'star rating' custom field type\n *\n * @since 0.1.0\n *\n * @param array  $field           \t\t\tThe passed in `CMB2_Field` object\n * @param mixed  $value       \t\t\t\tThe value of this field escaped.\n *                                   \t\t\t\t\tIt defaults to `sanitize_text_field`.\n *                                   \t\t\t\t\tIf you need the unescaped value, you can access it\n *                                   \t\t\t\t\tvia `$field->value()`\n * @param int    $object_id          \t\tThe ID of the current object\n * @param string $object_type        \tThe type of object you are working with.\n *                                  \t\t\t\t \tMost commonly, `post` (this applies to all post-types),\n *                                   \t\t\t\t\tbut could also be `comment`, `user` or `options-page`.\n * @param object $field_type_object  \tThe `CMB2_Types` object\n */\nfunction eh_cmb2_render_star_rating_field_callback( $field, $value, $object_id, $object_type, $field_type_object ) {\n\t// enqueue styles\n\twp_enqueue_style( 'star-rating-metabox-css', plugin_dir_url(__FILE__) . '/css/star-rating-field-type.css', array( 'cmb2-styles' ), 'all', false );\n\t?>\n\t\t<section id=\"cmb2-star-rating-metabox\">\n\t\t\t<fieldset>\n\t\t\t\t<span class=\"star-cb-group\">\n\t\t\t\t\t<?php\n\t\t\t\t\t\t$y = 5;\n\t\t\t\t\t\twhile( $y > 0 ) {\n\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t\t\t<input type=\"radio\" id=\"rating-<?php echo $y; ?>\" name=\"<?php echo $field_type_object->_id( false ); ?>\" value=\"<?php echo $y; ?>\" <?php checked( $value, $y ); ?>/>\n\t\t\t\t\t\t\t\t<label for=\"rating-<?php echo $y; ?>\"><?php echo $y; ?></label>\n\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\t$y--;\n\t\t\t\t\t\t}\n\t\t\t\t\t?>\n\t\t\t\t</span>\n\t\t\t</fieldset>\n\t\t</section>\n\t<?php\n\techo $field_type_object->_desc( true );\n\n}\nadd_filter( 'cmb2_render_star_rating', 'eh_cmb2_render_star_rating_field_callback', 10, 5 );"
  },
  {
    "path": "custom-field-types/taxonomy-radio-with-image-field-type/DJ_Taxonomy_Radio_Hierarchical_With_Image.php",
    "content": "<?php\n\n/**\n * Handles 'taxonomy_radio_with_image' custom field type.\n */\nclass DJ_Taxonomy_Radio_Hierarchical_With_Image extends CMB2_Type_Taxonomy_Radio_Hierarchical {\n\tprotected $term;\n\tprotected $saved_term;\n\n\t/**\n\t * Build children hierarchy.\n\t *\n\t * @param  object       $parent_term The parent term object.\n\t * @param  array|string $saved       Array of terms set to the object, or single term slug.\n\t *\n\t * @return string                    List of terms.\n\t */\n\tprotected function build_children( $parent_term, $saved ) {\n\t\tif ( empty( $parent_term->term_id ) ) {\n\t\t\treturn '';\n\t\t}\n\n\t\t$this->parent = $parent_term->term_id;\n\n\t\t$terms   = $this->get_terms();\n\t\t$options = '';\n\n\t\tif ( ! empty( $terms ) && is_array( $terms ) ) {\n\t\t\t// DJ - BEGIN\n\t\t\t$options = '<ul class=\"cmb2-indented-hierarchy\">';\n\t\t\t$options .= $this->loop_terms( $terms, $saved );\n\t\t\t$options .= '</ul>';\n\t\t\t// DJ - END\n\t\t}\n\n\t\treturn $options;\n\t}\n\n\tprotected function list_term_input( $term, $saved_term ) {\n\t\t$this->term = $term;\n\t\t$this->saved_term = $saved_term;\n\t\treturn parent::list_term_input( $term, $saved_term );\n\t}\n\n\tpublic function list_input( $args = array(), $i ) {\n\t\tif ( empty( $this->term ) ) {\n\t\t\treturn parent::list_input( $args, $i );\n\t\t}\n\n\t\t$a = $this->parse_args( 'list_input', array(\n\t\t\t'type'  => 'radio',\n\t\t\t'class' => 'cmb2-option',\n\t\t\t'name'  => $this->_name(),\n\t\t\t'id'    => $this->_id( $i ),\n\t\t\t'value' => $this->field->escaped_value(),\n\t\t\t'label' => '',\n\t\t), $args );\n\n\t\t$taxonomy  = $this->field->args( 'taxonomy' );\n\t\t$image     = '';\n\t\t$is_parent = '';\n\n\t\t$image_url = isset( $this->term->term_id ) ? get_term_meta( $this->term->term_id, 'yourprefix_category_avatar', true ) : '';\n\n\t\tif ( ! empty( $image_url ) && $this->term->parent == 0 ) {\n\t\t\t$image = '<img style=\"max-width: 30px; height: auto; vertical-align: middle; margin-right: 5px;\" src=\"'.$image_url.'\" alt=\"'.$a['label'].'\" />';\n\t\t} else {\n\t\t\t$image = '';\n\t\t}\n\n\t\t$atts = $this->concat_attrs( $a, array( 'label' ) );\n\t\tif ( isset( $this->term->term_id ) && get_term_children( $this->term->term_id, $taxonomy ) ) {\n\t\t\t$is_parent = 'class=\"parent\"';\n\t\t\treturn sprintf( \"\\t\" . '<li %s><span><input%s/><label for=\"%s\">%s<span>%s</span></label></span>' . \"\\n\", $is_parent, $atts, $a['id'], $image, $a['label'] );\n\t\t} else {\n\t\t\treturn sprintf( \"\\t\" . '<li %s><span><input%s/><label for=\"%s\">%s<span>%s</span></label></span></li>' . \"\\n\", $is_parent, $atts, $a['id'], $image, $a['label'] );\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "custom-field-types/taxonomy-radio-with-image-field-type/DJ_Taxonomy_Radio_With_Image_Display.php",
    "content": "<?php\n\n/**\n * Handles 'taxonomy_radio_with_image' custom field type.\n */\nclass DJ_Taxonomy_Radio_With_Image_Display extends CMB2_Display_Taxonomy_Radio {}\n"
  },
  {
    "path": "custom-field-types/taxonomy-radio-with-image-field-type/taxonomy-radio-with-image-field-type.php",
    "content": "<?php\n/**\n * Custom field type to add term images to the output of the taxonomy radio field.\n * See https://wordpress.org/support/topic/disable-cmb2-on-some-view/\n */\n\nfunction dj_taxonomy_radio_with_image_field_type_class_name() {\n\t$class_name = 'DJ_Taxonomy_Radio_Hierarchical_With_Image';\n\n\trequire_once dirname( __FILE__ ) . '/'. $class_name .'.php';\n\n\treturn $class_name;\n}\n\nfunction dj_taxonomy_radio_with_image_field_type_display_class_name() {\n\t$class_name = 'DJ_Taxonomy_Radio_With_Image_Display';\n\n\trequire_once dirname( __FILE__ ) . '/'. $class_name .'.php';\n\n\treturn $class_name;\n}\n\nfunction dj_add_taxonomy_radio_with_image( $types ) {\n\t$types[] = 'taxonomy_radio_with_image';\n\n\treturn $types;\n}\n\nfunction dj_taxonomy_radio_with_image_sanitize( $check, $meta_value, $object_id, $field_args, $sanitize_object ) {\n\t$sanitized_value = $sanitize_object->taxonomy();\n\treturn $sanitize_object->_is_empty_array( $sanitized_value ) ? '' : $sanitized_value;\n}\n\n\nfunction cmb2_init_taxonomy_radio_with_image_field_type() {\n\tadd_filter( 'cmb2_render_class_taxonomy_radio_with_image', 'dj_taxonomy_radio_with_image_field_type_class_name' );\n\tadd_filter( 'cmb2_display_class_taxonomy_radio_with_image', 'dj_taxonomy_radio_with_image_field_type_display_class_name' );\n\tadd_filter( 'cmb2_all_or_nothing_types', 'dj_add_taxonomy_radio_with_image' );\n\tadd_filter( 'cmb2_non_repeatable_fields', 'dj_add_taxonomy_radio_with_image' );\n\n\t/**\n\t * The following snippet is required for allowing the taxonomy_radio_with_image field\n\t * to save to the term fields.\n\t */\n\tadd_filter( 'cmb2_sanitize_taxonomy_radio_with_image', 'dj_taxonomy_radio_with_image_sanitize', 10, 5 );\n}\nadd_action( 'cmb2_init', 'cmb2_init_taxonomy_radio_with_image_field_type' );\n"
  },
  {
    "path": "custom-field-types/textarea-with-checkbox.php",
    "content": "<?php\n\n/**\n * CMB2 Multicheck by Post Type\n *\n * @package CMB2 Default Tags field/metabox\n * @author Daniele Mte90 Scasciafratte\n */\n\nif ( !defined( 'ABSPATH' ) ) exit;\n\nif ( ! class_exists( 'CMB2_Field_Textarea_With_Checkbox' ) ) {\n  /**\n   * Class CMB2_Field_Textarea_With_Checkbox\n   */\n  class CMB2_Field_Textarea_With_Checkbox  {\n\n    /**\n     * Current version number\n     */\n    const VERSION = '1.0.0';\n    /**\n     * Initialize the plugin\n     */\n    public function __construct() {\n       add_action( 'cmb2_render_textarea_with_checkbox', [$this, 'render_textarea_with_checkbox'], 10, 5 );\n       add_filter( 'cmb2_sanitize_textarea_with_checkbox', [$this, 'sanitize_textarea_with_checkbox'], 10, 5 );\n       add_filter( 'cmb2_types_esc_textarea_with_checkbox', [$this, 'escape_textarea_with_checkbox'], 10, 4 );\n    }\n\n    public function render_textarea_with_checkbox( $field, $field_escaped_value, $field_object_id, $field_object_type, $field_type_object ) {\n      // the properties of the fields.\n      $field_escaped_value = wp_parse_args( $field_escaped_value, [\n        'text'\t=> '',\n        'status'\t=> '',\n      ] );\n      $checked = false;\n      if ( ! empty( $field_escaped_value['status'] ) ) {\n        $checked = true;\n      }\n      ?>\n      <div style=\"overflow: hidden;\">\n        <?= $field_type_object->textarea( [\n          'name' => $field_type_object->_name( '[text]' ),\n          'id' => $field_type_object->_id( '_text' ),\n          'value' => $field_escaped_value['text'],\n        ] ); ?>\n      </div>\n      <div style=\"overflow: hidden\">\n        <p><label for=\"<?= $field_type_object->_id( '_status' ); ?>\"><?= $field_type_object->field->args('title_checkbox'); ?></label></p>\n        <?= $field_type_object->checkbox( [\n          'type' => 'checkbox',\n          'name' => $field_type_object->_name( '[status]' ),\n          'id' => $field_type_object->_id( '_status' ),\n        ], $checked ); ?>\n      </div>\n      <?php\n      echo $field_type_object->_desc( true );\n    }\n\n    /**\n     * Sanitize Field.\n     */\n    public static function sanitize_textarea_with_checkbox( $check, $meta_value, $object_id, $field_args, $sanitize_object ) {\n      if ( !is_array( $meta_value ) || !( array_key_exists('repeatable', $field_args ) && $field_args['repeatable'] == TRUE ) ) {\n        return $check;\n      }\n      \n      $new_values = array();\n      foreach ( $meta_value as $key => $val ) {\n        if( !empty( $meta_value[$key]['text'] ) ) {\n            $new_values[$key] = array_filter( array_map( 'sanitize_text_field', $val ) );\n        }\n      }\n      \n      return array_filter( array_values( $new_values ) );\n    }\n\n    /**\n     * Escape Field.\n     */\n    public static function escape_textarea_with_checkbox( $check, $meta_value, $field_args, $field_object ) {\n      if ( !is_array( $meta_value ) || ! $field_args['repeatable'] ) {\n        return $check;\n      }\n\n      $new_values = array();\n      foreach ( $meta_value as $key => $val ) {\n        if( !empty( $meta_value[$key]['text'] ) ) {\n            $new_values[$key] = array_filter( array_map( 'esc_attr', $val ) );\n        }\n      }\n      \n      return array_filter( array_values( $new_values ) );\n    }\n  }\n\n  $cmb2_field_textarea_with_checkbox = new CMB2_Field_Textarea_With_Checkbox();\n}\n"
  },
  {
    "path": "custom-field-types/year-range-field-type.php",
    "content": "<?php\n/*\n * Year Range field type.. two year-pickers, start and end.\n * Screenshot: http://b.ustin.co/15lKk\n */\n\n/**\n * Example field registration:\n *\n * function yourprefix_register_demo_metabox() {\n *\n *    $cmb_demo = new_cmb2_box( array(\n *        'id'            => 'demo_metabox',\n *        'title'         => __( 'Test Metabox', 'cmb2' ),\n *        'object_types'  => array( 'page', ),\n *    ) );\n *\n *    $cmb_demo->add_field( array(\n *        'name'     => __( 'Date Year Range', 'cmb2' ),\n *        'desc'     => __( 'field description (optional)', 'cmb2' ),\n *        'id'       => 'yourprefix_demo_date_year_range',\n *        'type'     => 'date_year_range',\n *        // Optionally set default values.\n *        'options'  => array(\n *             'earliest' => 1930, // Set the earliest year that should be shown.\n *            // 'start_reverse_sort' => true,\n *            // 'finish_reverse_sort' => false,\n *        ),\n *        'default'  => array(\n *             'start'  => 1930,\n *             'finish' => 'current',\n *        ),\n *        // 'text'     => array(\n *        //    'start_label'  => 'Start', // Optionally change start text.\n *        //    'finish_label' => 'Finish', // Optionally change finish text.\n *        //    'separator' => ' to ', // Optionally change separator string/text.\n *        // ),\n *        // 'split_values' => true, // Split values to sep. meta fields.\n *    ) );\n *\n * }\n * add_action( 'cmb2_init', 'yourprefix_register_demo_metabox' );\n */\n\n\n/**\n * Render 'date_year_range' custom field type\n *\n * @since 0.1.0\n *\n * @param array  $field        The passed in `CMB2_Field` object\n * @param mixed  $value        The value of this field escaped.\n *                             It defaults to `sanitize_text_field`.\n *                             If you need the unescaped value, you can access it\n *                             via `$field->value()`\n * @param int    $object_id    The ID of the current object\n * @param string $object_type  The type of object you are working with.\n *                             Most commonly, `post` (this applies to all post-types),\n *                             but could also be `comment`, `user` or `options-page`.\n * @param object $type_object The `CMB2_Types` object\n */\nfunction jt_cmb2_date_year_range( $field, $value, $object_id, $object_type, $type_object ) {\n\t$earliest = $field->options( 'earliest' );\n\t$earliest = $earliest ? absint( $earliest ) : 1900;\n\n\t$start_reverse_sort = $field->options( 'start_reverse_sort' );\n\t$start_reverse_sort = $start_reverse_sort ? true : false;\n\n\t$finish_reverse_sort = $field->options( 'finish_reverse_sort' );\n\t$finish_reverse_sort = $finish_reverse_sort ? true : false;\n\n\t$value = wp_parse_args( $value, array(\n\t\t'start'  => '',\n\t\t'finish' => '',\n\t) );\n\n\t$desc = $field->args( 'description' );\n\t$field->args['description'] = '';\n\t$type_object->type = new CMB2_Type_Select( $type_object );\n\n\techo '<em>'. $type_object->_text( 'start_label', 'Starting Year' ) . '</em> ';\n\n\t$start_options = jt_cmb2_date_year_range_options( $type_object, $earliest, $value['start'], $start_reverse_sort );\n\techo $type_object->select( array(\n\t\t'name'    => $type_object->_name( '[start]' ),\n\t\t'id'      => $type_object->_id( '_start' ),\n\t\t'value'   => $value['start'],\n\t\t'class'   => 'cmb2_select cmb2-year-range-start',\n\t\t'options' => $start_options,\n\t\t'desc'    => '',\n\t) );\n\n\techo $type_object->_text( 'separator', ' &mdash; ' );\n\n\t$end_options = jt_cmb2_date_year_range_options( $type_object, $earliest, $value['finish'], $finish_reverse_sort  );\n\techo $type_object->select( array(\n\t\t'name'    => $type_object->_name( '[finish]' ),\n\t\t'id'      => $type_object->_id( '_finish' ),\n\t\t'value'   => $value['finish'],\n\t\t'class'   => 'cmb2_select cmb2-year-range-end',\n\t\t'options' => $end_options,\n\t\t'desc'    => '',\n\t) );\n\techo ' <em>'. $type_object->_text( 'finish_label', 'Final Year' ) . '</em>';\n\n\t$field->args['description'] = $desc;\n\n\t$type_object->_desc( true, true );\n\n\tadd_action( is_admin() ? 'admin_footer' : 'wp_footer', 'jt_cmb2_date_year_range_js' );\n\n}\nadd_filter( 'cmb2_render_date_year_range', 'jt_cmb2_date_year_range', 10, 5 );\n\nfunction jt_cmb2_date_year_range_js() {\n\tstatic $done = false;\n\tif ( ! $done ) {\n\t\t$done = true;\n\t}\n\t?>\n\t<script type=\"text/javascript\">\n\t\tjQuery( function( $ ) {\n\t\t\tvar $options = {};\n\n\t\t\tfunction makeSelected( $newoptions, selected ) {\n\t\t\t\tvar $selected = $newoptions.filter( '[value=\"'+ selected +'\"]' );\n\n\t\t\t\tif ( ! $selected.length ) {\n\t\t\t\t\t$selected = $newoptions.filter( '[value=\"\"]' );\n\t\t\t\t}\n\n\t\t\t\t// Clear out previous selected\n\t\t\t\t$newoptions.filter( ':selected' ).prop( 'selected', false );\n\n\t\t\t\t$selected.prop( 'selected', true );\n\n\t\t\t\treturn $newoptions;\n\t\t\t}\n\n\t\t\tfunction getNewOptions( $options, start, selected ) {\n\t\t\t\tif ( start ) {\n\t\t\t\t\t$options = $options.filter( function() {\n\t\t\t\t\t\tvar val = $( this ).val();\n\t\t\t\t\t\treturn'current' === start ? 'current' === val || '' === val : ( val >= start || ! val );\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\treturn makeSelected( $options.clone(), selected );\n\t\t\t}\n\n\t\t\t$( document.body ).on( 'change', '.cmb2-year-range-start', function() {\n\t\t\t\tvar $this        = $( this );\n\t\t\t\tvar start        = $this.find( ':selected' ).val();\n\t\t\t\tvar $endPicker   = $this.parent().find( '.cmb2-year-range-end' );\n\t\t\t\tvar id           = $endPicker.attr( 'id' );\n\t\t\t\tvar selectedEnd  = $endPicker.find( ':selected' ).val();\n\n\t\t\t\tif ( ! $options[ id ] ) {\n\t\t\t\t\t// Store a cached version of the unmodified options.\n\t\t\t\t\t$options[ id ] = $endPicker.find( 'option' ).clone();\n\t\t\t\t}\n\n\t\t\t\t$endPicker.html( getNewOptions( $options[ id ], start, selectedEnd ) );\n\t\t\t} );\n\n\t\t\t// Kick it off.\n\t\t\t$( '.cmb2-year-range-start' ).trigger( 'change' );\n\t\t});\n\t</script>\n\t<?php\n}\n\nfunction jt_cmb2_date_year_range_options( $type_object, $earliest, $value, $reverse = false ) {\n\t$options = array();\n\n\t$not_set = array(\n\t\t'value' => '',\n\t\t'label' => __( 'Not Set' ),\n\t);\n\n\tif ( cmb2_utils()->isempty( $value ) ) {\n\t\t$not_set['checked'] = 'checked';\n\t}\n\n\tfor ( $i = $earliest; $i <= date( 'Y' ); $i++ ) {\n\n\t\t$a = array( 'value' => $i, 'label' => $i );\n\t\tif ( absint( $value ) === $i ) {\n\t\t\t$a['checked'] = 'checked';\n\t\t}\n\n\t\t$options[] = $a;\n\t}\n\n\t$a = array(\n\t\t'value' => 'current',\n\t\t'label' => __( 'Current' ),\n\t);\n\n\tif ( 'current' === $value ) {\n\t\t$a['checked'] = 'checked';\n\t}\n\n\t$options[] = $a;\n\n\tif ( $reverse ) {\n\t\t$options = array_reverse( $options );\n\t}\n\n\tarray_unshift( $options, $not_set );\n\n\treturn implode( \"\\n\", array_map( array( $type_object, 'select_option' ), $options ) );\n}\n\n/**\n * Optionally save the values into separate fields.\n */\nfunction jt_cmb2_date_year_range_split_values( $override_value, $value, $object_id, $field_args ) {\n\tif ( ! isset( $field_args['split_values'] ) || ! $field_args['split_values'] ) {\n\t\t// Don't do the override\n\t\treturn $override_value;\n\t}\n\n\t$keys = array( 'start', 'finish' );\n\n\tforeach ( $keys as $key ) {\n\t\tif ( ! empty( $value[ $key ] ) ) {\n\t\t\tupdate_post_meta( $object_id, $field_args['id'] . '_'. $key, $value[ $key ] );\n\t\t}\n\t}\n\n\t// Tell CMB2 we already did the update\n\treturn true;\n}\nadd_filter( 'cmb2_sanitize_date_year_range', 'jt_cmb2_date_year_range_split_values', 12, 4 );\n\n/**\n * Optionally fetch the values from separate fields as well.\n */\nfunction jt_cmb2_date_year_range_get_split_values( $no_override, $object_id, $args, $field ) {\n\tif ( 'date_year_range' !== $field->args( 'type' ) || ! $field->args( 'split_values' ) ) {\n\t\treturn $no_override;\n\t}\n\n\t$value = array(\n\t\t'start'  => get_post_meta( $object_id, $args['field_id'] . '_start', 1 ),\n\t\t'finish' => get_post_meta( $object_id, $args['field_id'] . '_finish', 1 ),\n\t);\n\n\treturn $value;\n}\nadd_filter( 'cmb2_override_meta_value', 'jt_cmb2_date_year_range_get_split_values', 10, 4 );\n\n/**\n * The following snippets are required for allowing the date_year_range field\n * to work as a repeatable field, or in a repeatable group\n */\n\nfunction jt_cmb2_sanitize_date_year_range( $check, $meta_value, $object_id, $field_args, $sanitizer ) {\n\n\t// if not repeatable, bail out.\n\tif ( ! is_array( $meta_value ) || ! $field_args['repeatable'] ) {\n\t\treturn $check;\n\t}\n\n\tforeach ( $meta_value as $key => $val ) {\n\t\t$meta_value[ $key ] = array_filter( array_map( 'sanitize_text_field', $val ) );\n\t}\n\n\treturn array_filter( $meta_value );\n}\nadd_filter( 'cmb2_sanitize_date_year_range', 'jt_cmb2_sanitize_date_year_range', 10, 5 );\n\nfunction jt_cmb2_esc_date_year_range( $check, $meta_value, $field_args, $field_object ) {\n\n\t// if not repeatable, bail out.\n\tif ( ! is_array( $meta_value ) || ! $field_args['repeatable'] ) {\n\t\treturn $check;\n\t}\n\n\tforeach ( $meta_value as $key => $val ) {\n\t\t$meta_value[ $key ] = array_filter( array_map( 'esc_attr', $val ) );\n\t}\n\n\treturn array_filter( $meta_value );\n}\nadd_filter( 'cmb2_types_esc_date_year_range', 'jt_cmb2_esc_date_year_range', 10, 4 );\n"
  },
  {
    "path": "filters-and-actions/README.md",
    "content": "CMB2 Filters and Actions\n==========\n\nCMB2 has many filters and actions. Included here are snippets which demonstrate some handy ways to use those hooks."
  },
  {
    "path": "filters-and-actions/cmb2-add-fields-dynamically.php",
    "content": "<?php\n/**\n * This file demonstrates adding new fields dynamically, based on the existing post meta.\n * @link https://geek.hellyer.kiwi/2018/08/11/dynamically-controlling-cmb2-metaboxes/\n */\n\nadd_action( 'cmb2_admin_init', 'register_dynamic_fields_box' );\nfunction register_dynamic_fields_box() {\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'dynamic_fields_box',\n\t\t'title'        => 'Some test metaboxes',\n\t\t'object_types' => array( 'page', 'post' ),\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name' => 'Set number of next item',\n\t\t'id'   => 'number_of_next_item',\n\t\t'type' => 'text',\n\t\t'default' => '1',\n\t\t'attributes' => array(\n\t\t\t'type' => 'number',\n\t\t\t'pattern' => '\\d*',\n\t\t),\n\t\t'sanitization_cb' => 'absint',\n\t\t'escape_cb'       => 'absint',\n\t) );\n\n\t// Add dynamic fields during normal view.\n\tadd_action( 'cmb2_init_hookup_dynamic_fields_box', 'add_fields_dynamically_to_box' );\n\n\t// Add dynamic fields during save process.\n\tadd_action( 'cmb2_post_process_fields_dynamic_fields_box', 'add_fields_dynamically_to_box' );\n}\n\nfunction add_fields_dynamically_to_box( $cmb ) {\n\tif ( $cmb->object_id() ) {\n\t\t$position = 2;\n\n\t\t// Loop through however many items are selected in previous field\n\t\t$number_of_items = get_post_meta( $cmb->object_id(), 'number_of_next_item', true );\n\t\t$number = 1;\n\t\twhile ( $number <= $number_of_items ) {\n\n\t\t\t$cmb->add_field( array(\n\t\t\t\t'name' =>  'Item #' . $number,\n\t\t\t\t'desc'   => 'item_' . $number,\n\t\t\t\t'id'   => 'item_' . $number,\n\t\t\t\t'type' => 'text',\n\t\t\t), $position++ );\n\n\t\t\t$number++;\n\t\t}\n\t}\n}"
  },
  {
    "path": "filters-and-actions/cmb2_all_or_nothing_types-filter.php",
    "content": "<?php\n\n/**\n * CMB2 has a defined set of \"all or nothing types\".\n * This filter allows us to change that.\n */\n\nfunction add_select2_to_all_or_nothing_types( $types ) {\n\t$types[] = 'select2';\n\treturn $types;\n}\nadd_filter( 'cmb2_all_or_nothing_types', 'add_select2_to_all_or_nothing_types' );\n"
  },
  {
    "path": "filters-and-actions/cmb2_before_form-and-cmb2_after_form-hooks.php",
    "content": "<?php\n/**\n * This file demonstrates using the following hooks:\n * cmb2_before_form and cmb2_before_{$object_type}_form_{$cmb_id}\n * Docs: https://github.com/CMB2/CMB2/blob/a7b93886e56a9c9f6e78ea0efbe1b01bcbb1a248/includes/CMB2.php#L245-L270\n * cmb2_after_form and cmb2_after_{$object_type}_form_{$cmb_id}\n * Docs: https://github.com/CMB2/CMB2/blob/a7b93886e56a9c9f6e78ea0efbe1b01bcbb1a248/includes/CMB2.php#L361-L385\n */\n\nfunction yourprefix_add_html_before_cmb2_output( $cmb_id, $object_id, $object_type, $cmb ) {\n\t// Only output above the _yourprefix_demo_metabox metabox.\n\tif ( '_yourprefix_demo_metabox' !== $cmb_id ) {\n\t\treturn;\n\t}\n\n\techo '<div class=\"my-custom-wrapper\">';\n\techo '<h4>Welcome to my metabox!</h4>';\n\n\tadd_action( 'cmb2_after_form', 'yourprefix_add_html_after_cmb2_output', 10, 4 );\n}\nadd_action( 'cmb2_before_form', 'yourprefix_add_html_before_cmb2_output', 10, 4 );\n\nfunction yourprefix_add_html_after_cmb2_output( $cmb_id, $object_id, $object_type, $cmb ) {\n\techo '</div><!-- .my-custom-wrapper-->';\n}\n\n\n// This can be done a bit more simply this way:\n\nfunction add_html_before_yourprefix_demo_metabox_output( $cmb_id, $object_id, $object_type, $cmb ) {\n\techo '<div class=\"my-custom-wrapper\">';\n\techo '<h4>Welcome to my metabox!</h4>';\n\n\tadd_action( \"cmb2_after_{$object_type}_form_{$cmb_id}\", 'add_html_after_yourprefix_demo_metabox_output', 10, 4 );\n}\n// Only output above the _yourprefix_demo_metabox metabox.\nadd_action( 'cmb2_before_post_form__yourprefix_demo_metabox', 'add_html_before_yourprefix_demo_metabox_output', 10, 4 );\n\nfunction add_html_after_yourprefix_demo_metabox_output( $cmb_id, $object_id, $object_type, $cmb ) {\n\techo '</div><!-- .my-custom-wrapper-->';\n}\n"
  },
  {
    "path": "filters-and-actions/cmb2_init_$cmb_id-add-fields.php",
    "content": "<?php\n/**\n * This file demonstrates adding new fields to a registered CMB2 metaboxes during its \"cmb2_init_{$cmb_id}\" hook\n */\n\nfunction yourprefix_add_new_field_to_top_of_demo_metabox( $cmb ) {\n\n\t$cmb->add_field(\n\t\tarray(\n\t\t\t'name' => __( 'New at the top', 'your_textdomain' ),\n\t\t\t'desc' => __( 'Using the \"cmb2_init_{$cmb_id}\" hook', 'your_textdomain' ),\n\t\t\t'id'   => '_new_at_the_top',\n\t\t\t'type' => 'text',\n\t\t),\n\t\t1\n\t);\n\n}\nadd_action( 'cmb2_init__yourprefix_demo_metabox', 'yourprefix_add_new_field_to_top_of_demo_metabox' );\n"
  },
  {
    "path": "filters-and-actions/cmb2_init_$cmb_id-modify-object-types.php",
    "content": "<?php\n/**\n * This snippet demonstrates modifying (during its \"cmb2_init_{$cmb_id}\" hook) the object-types\n * registered to a CMB2 box.\n *\n * The \"cmb2_init_{$cmb_id}\" hook occurs during the initiation of the CMB2 object.\n *\n * The dynamic portion of the hook name, `$cmb_id`, is the registered cmb2 box id,\n * so in our example case: \"yourprefix_demo_metabox\".\n */\nfunction yourprefix_demo_metabox_modify_object_types( $cmb ) {\n\n\t$types = $cmb->box_types();\n\n\t$types[] = 'books'; // Your custom post type slug.\n\n\t// Bam.\n\t$cmb->set_prop( 'object_types', $types );\n}\nadd_action( 'cmb2_init_yourprefix_demo_metabox', 'yourprefix_demo_metabox_modify_object_types' );\n"
  },
  {
    "path": "filters-and-actions/cmb2_init_$cmb_id-remove-field.php",
    "content": "<?php\n/**\n * This file demonstrates removing fields from a registered CMB2 metaboxes during its \"cmb2_init_{$cmb_id}\" hook\n */\n\nfunction yourprefix_remove_field_from_demo_metabox( $cmb ) {\n\t$cmb->remove_field( '_yourprefix_demo_textsmall' );\n}\nadd_action( 'cmb2_init__yourprefix_demo_metabox', 'yourprefix_remove_field_from_demo_metabox' );\n"
  },
  {
    "path": "filters-and-actions/cmb2_init_$cmb_id-replace-field.php",
    "content": "<?php\n/**\n * This file demonstrates adding replacing a registered field in the CMB2 metaboxe\n * with an id of `_yourprefix_demo_metaboxx`,  during its \"cmb2_init_hookup_{$cmb_id}\" hook.\n */\n\nfunction yourprefix_replace_field_in_demo_metaboxx( $cmb ) {\n\n\t// Should we replace the field with our new one?\n\t$some_condition = true;\n\n\tif ( $some_condition ) {\n\t\t$cmb->remove_field( '_yourprefix_demo_textsmall' );\n\n\t\t$cmb->add_field(\n\t\t\tarray(\n\t\t\t\t'name' => __( 'REPLACED Text Small Field', 'cmb2' ),\n\t\t\t\t'desc' => __( 'Using the \"cmb2_init_{$cmb_id}\" hook', 'your_textdomain' ),\n\t\t\t\t'id'   => '_yourprefix_demo_textsmall',\n\t\t\t\t'type' => 'text_money',\n\t\t\t),\n\t\t\t17 /* This needs to be the nth position of the original field */\n\t\t);\n\t}\n\n}\nadd_action( 'cmb2_init_hookup__yourprefix_demo_metaboxx', 'yourprefix_replace_field_in_demo_metaboxx', 999 );\n"
  },
  {
    "path": "filters-and-actions/cmb2_init_before_hookup-add-fields.php",
    "content": "<?php\n/**\n * This file demonstrates adding new fields to registered CMB2 metaboxes/objects\n */\n\nfunction yourprefix_add_new_field_in_3rd_position() {\n\n\t$prefix = '_yourprefix_demo_';\n\n\t// Retrieve a CMB2 instance\n\t$cmb = cmb2_get_metabox( '_yourprefix_demo_metabox' );\n\n\t// This should return false because we don't have a '_yourprefix_demo_text2' field\n\t$field_id = $cmb->update_field_property( '_yourprefix_demo_text2', 'type', 'text' );\n\n\t/**\n\t * Since '_yourprefix_demo_text2' doesn't exist, Let's create it.\n\t * Always need to compare this value strictly to false, as a field_id COULD be 0 or ''\n\t */\n\tif ( false === $field_id ) {\n\t\t$cmb->add_field(\n\t\t\t// Normal field setup\n\t\t\tarray(\n\t\t\t\t'name'       => __( 'Test Text 2', 'your_textdomain' ),\n\t\t\t\t'desc'       => __( 'Test Text 2 description', 'your_textdomain' ),\n\t\t\t\t'id'         => '_yourprefix_demo_text2',\n\t\t\t\t'type'       => 'text',\n\t\t\t\t'attributes' => array( 'placeholder' => __( \"I'm some placeholder text\", 'your_textdomain' ) ),\n\t\t\t),\n\t\t\t3 // Insert this field in the third position\n\t\t);\n\n\t}\n\n}\nadd_action( 'cmb2_init_before_hookup', 'yourprefix_add_new_field_in_3rd_position' );\n\nfunction yourprefix_add_new_field_to_group() {\n\t// Try to get a metabox w/ the id of '_yourprefix_group_metabox'\n\tif ( $cmb_group_demo = cmb2_get_metabox( '_yourprefix_group_metabox' ) ) {\n\n\t\t$cmb_group_demo->add_group_field(\n\t\t\tarray(\n\t\t\t\t'name'       => __( 'Test Text 2', 'your_textdomain' ),\n\t\t\t\t'desc'       => __( 'field description (optional)', 'your_textdomain' ),\n\t\t\t\t'id'         => 'text2',\n\t\t\t\t'type'       => 'text',\n\t\t\t\t'attributes' => array( 'placeholder' => __( \"I'm some placeholder text\", 'your_textdomain' ) ),\n\t\t\t),\n\t\t\t'_yourprefix_group_demo', // Add this to the _yourprefix_group_demo group field\n\t\t\t2 // And insert it into the 2nd position\n\t\t);\n\n\t}\n }\nadd_action( 'cmb2_init_before_hookup', 'yourprefix_add_new_field_to_group' );\n"
  },
  {
    "path": "filters-and-actions/cmb2_init_before_hookup-remove-cmb2-metabox.php",
    "content": "<?php\n/**\n * You can remove an entire CMB2 metabox. But be polite.\n */\nfunction yourprefix_remove_metabox() {\n\t// to remove a cmb metabox.. use wisely\n\tCMB2_Boxes::remove( '_yourprefix_demo_metabox' );\n}\nadd_action( 'cmb2_init_before_hookup', 'yourprefix_remove_metabox' );\n"
  },
  {
    "path": "filters-and-actions/cmb2_init_before_hookup-update-existing-fields.php",
    "content": "<?php\n/**\n * This file demonstrates modifying existing registered CMB2 metabox fields\n */\n\nfunction yourprefix_update_fields_properties() {\n\t// Retrieve a CMB2 instance\n\t$cmb = cmb2_get_metabox( '_yourprefix_demo_metabox' );\n\n\t/**\n\t * Update a property on the '_yourprefix_demo_text' field.\n\t * In this case, we'll remove the show_on_cb conditional callback\n\t * (to instead always display the field)\n\t *\n\t * If field exists, and property updated, it will return the field id\n\t */\n\t$field_id = $cmb->update_field_property( '_yourprefix_demo_text', 'show_on_cb', false );\n\n\t/**\n\t * Always need to compare this value strictly to false, as a field_id COULD be 0 or ''\n\t */\n\tif ( false !== $field_id ) {\n\n\t\t/**\n\t\t * Because we don't want to 'stomp' a field's 'attributes' property\n\t\t * (It may already have some attributes), we're going to get\n\t\t * the field's attributes property and append to it.\n\t\t */\n\n\t\t// Get all fields for this metabox\n\t\t$fields = $cmb->prop( 'fields' );\n\n\t\t// Get the attributes array if it exists, or else create it\n\t\t$attributes = isset( $fields['_yourprefix_demo_text']['attributes'] )\n\t\t\t? $fields['_yourprefix_demo_text']['attributes']\n\t\t\t: array();\n\n\t\t// Add placeholder text\n\t\t$attributes['placeholder'] = __( \"I'm some placeholder text\", 'your_textdomain' );\n\n\t\t// Update the field's 'attributes' property\n\t\t$cmb->update_field_property( '_yourprefix_demo_text', 'attributes', $attributes );\n\n\t}\n\n}\nadd_action( 'cmb2_init_before_hookup', 'yourprefix_update_fields_properties' );\n"
  },
  {
    "path": "filters-and-actions/cmb2_override_{$field_id}_meta_value-filter.php",
    "content": "<?php\n/**\n * This file demonstrates providing a default value for a group field from a JSON blob.\n * It is using the fields from the group field example in:\n * [example-functions.php](https://github.com/WebDevStudios/CMB2/blob/3b0cc0a696f70d1f3ce7c94af8318f4dd3f34688/example-functions.php#L460-L522).\n */\n\n/**\n * Filters the value for our group field.\n *\n * @param mixed $value     The value get_metadata() should\n *                         return - a single metadata value,\n *                         or an array of values.\n *\n * @param int   $object_id Object ID.\n *\n * @param array $args {\n *     An array of arguments for retrieving data\n *\n *     @type string $type     The current object type\n *     @type int    $id       The current object ID\n *     @type string $field_id The ID of the field being requested\n *     @type bool   $repeat   Whether current field is repeatable\n *     @type bool   $single   Whether current field is a single database row\n * }\n *\n * @param CMB2_Field object $field This field object\n *\n * @return array The group field value array.\n */\nfunction yourprefix_get_default_group_value_from_json( $value, $object_id, $args, $field ) {\n\tstatic $defaults = null;\n\n\t// Only set the default if the original value has not been overridden,\n\t// and if there is no existing value.\n\tif ( 'cmb2_field_no_override_val' !== $value ) {\n\t\treturn $value;\n\t}\n\n\t// Get the value for the field.\n\t$data = 'options-page' === $args['type']\n\t\t? cmb2_options( $args['id'] )->get( $args['field_id'] )\n\t\t: get_metadata( $args['type'], $args['id'], $args['field_id'], ( $args['single'] || $args['repeat'] ) );\n\n\t// Get the default values from JSON\n\tif ( null === $defaults ) {\n\t\t// Get your JSON blob.. hard-coded for demo.\n\t\t$json = '[{\"description\":\"This is a <strong>description<\\/strong>\",\"image\":\"\\/wp-content\\/uploads\\/2016\\/10\\/default-image-1.jpg\",\"image_id\":663},{\"title\":\"2nd Title\",\"description\":\"This is a second <strong>description<\\/strong>\",\"image\":\"\\/wp-content\\/uploads\\/2016\\/10\\/default-image-2.jpg\",\"image_id\":655,\"image_caption\":\"This is an image caption.\"}]';\n\n\t\t$defaults = json_decode( $json, 1 );\n\t}\n\n\t// Set our group field value to the default.\n\t$value = $defaults;\n\n\t// If the group field's retrieved value is not empty...\n\tif ( ! empty( $data ) ) {\n\t\t$value = array();\n\t\t// Then loop the defaults and mash the field's value up w/ the default.\n\t\tforeach ( $defaults as $key => $default_group_val ) {\n\t\t\t$value[ $key ] = isset( $data[ $key ] )\n\t\t\t\t? wp_parse_args( $data[ $key ], $default_group_val )\n\t\t\t\t: $default_group_val;\n\t\t}\n\t}\n\n\treturn $value;\n}\n\nadd_filter( 'cmb2_override_yourprefix_group_demo_meta_value', 'yourprefix_get_default_group_value_from_json', 10, 4 );\n"
  },
  {
    "path": "filters-and-actions/custom-css-for-specific-metabox.php",
    "content": "<?php\n/**\n * Add custom CSS which only loads for a particular metabox\n */\n\n/**\n * Setup the metabox\n */\nfunction js_custom_css_for_metabox() {\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'custom_css_test',\n\t\t'title'        => __( 'Custom CSS Test', 'cmb2' ),\n\t\t'object_types' => array( 'page', ),\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'id'          => '_cmb2_test_text',\n\t\t'type'        => 'text',\n\t) );\n}\nadd_action( 'cmb2_admin_init', 'js_custom_css_for_metabox' );\n\nfunction js_add_custom_css_for_metabox( $post_id, $cmb ) {\n\t?>\n\t<style type=\"text/css\" media=\"screen\">\n\t\t#custom_css_test .regular-text {\n\t\t  width: 99%;\n\t\t}\n\t</style>\n\t<?php\n}\n\n$object = 'post'; // post | term\n$cmb_id = 'custom_css_test';\nadd_action( \"cmb2_after_{$object}_form_{$cmb_id}\", 'js_add_custom_css_for_metabox', 10, 2 );\n"
  },
  {
    "path": "filters-and-actions/disable-styles-on-front-end-forms.php",
    "content": "<?php\n/**\n * This file demonstrates how to disable CMB2 styles on all front end forms.\n * \n * It’s important, when using the `cmb2_enqueue_*` filters to only disable for your\n * specific conditions/scenarios. It’s entirely possible that another plugin/theme is\n * using CMB2 and *needs* the styles on the front-end, so blindly disabling can cause issues.\n */\n\nfunction yourprefix_disable_cmb2_front_end_styles( $enabled ) {\n\n\tif ( ! is_admin() /* && meets_your_specific_conditions() */ ) {\n\t\t$enabled = false;\n\t}\n\n\treturn $enabled;\n}\nadd_filter( 'cmb2_enqueue_css', 'yourprefix_disable_cmb2_front_end_styles' );\n"
  },
  {
    "path": "filters-and-actions/localize-date-format.php",
    "content": "<?php\n\n/**\n * A demo of the helper function used to \n * localise any date picker form in CMB2.\n * See http://api.jqueryui.com/datepicker/\n * for more info.\n * Refer to the CMB Field Types Wiki entry\n * if you wish to implement a different date format\n * per meta field using date_format.\n * This snippet was modified from one at the following link:\n * https://github.com/WebDevStudios/CMB2/issues/73\n */\n\nadd_filter( 'cmb2_localized_data', 'prefix_cmb_set_date_format' );\nfunction prefix_cmb_set_date_format( $l10n ) {\n\t// Set your date format replacing 'dd-mm-yy' (UK format)\n    $l10n['defaults']['date_picker']['dateFormat'] = 'dd-mm-yy';\n    return $l10n;\n}"
  },
  {
    "path": "filters-and-actions/override-cmb2-data-source.php",
    "content": "<?php\n/**\n * Demonstrates using an alternate data source for storing/retrieving data for a CMB2 box.\n * In this case, we're simply storing/retrieving/removing from an option in the options table,\n * but this can be extended to handle data storage to/from custom tables, Redis, REST APIs, etc.\n */\n\nadd_action( 'cmb2_init', 'yourprefix_register_yourprefix_group_alt_data_metabox' );\n/**\n * Hook in and add a metabox to demonstrate repeatable grouped fields\n */\nfunction yourprefix_register_yourprefix_group_alt_data_metabox() {\n\n\t$default_values = get_option( 'yourprefix_group_alt_data_demo' );\n\tif ( ! is_array( $default_values ) ) {\n\t\tadd_option( 'yourprefix_group_alt_data_demo', array(\n\t\t\t// Default group 1.\n\t\t\tarray(\n\t\t\t\t'title' => 'Hey, this is your first entry',\n\t\t\t\t'description' => 'Go ahead and delete this entry, or update its contents',\n\t\t\t),\n\t\t\t// Default group 2.\n\t\t\tarray(\n\t\t\t\t'title' => 'Hey, this is your 2nd entry',\n\t\t\t\t'description' => '#2',\n\t\t\t),\n\t\t) );\n\t}\n\n\t$cmb_group = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_group_alt_data_metabox',\n\t\t'title'        => __( 'Repeating Field Group', 'cmb2' ),\n\t\t'object_types' => array( 'post', ),\n\t\t'show_on'      => array( 'id' => array( 1000, ) ),\n\t\t'show_in_rest' => WP_REST_Server::ALLMETHODS,\n\t) );\n\n\t// $group_field_id is the field id string, so in this case: '_yourprefix_group_demo'\n\t$group_field_id = $cmb_group->add_field( array(\n\t\t'id'          => 'yourprefix_group_alt_data_demo',\n\t\t'type'        => 'group',\n\t\t'options'     => array(\n\t\t\t'group_title'   => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number\n\t\t\t'add_button'    => __( 'Add Another Entry', 'cmb2' ),\n\t\t\t'remove_button' => __( 'Remove Entry', 'cmb2' ),\n\t\t\t'sortable'      => true, // beta\n\t\t),\n\t) );\n\n\t/**\n\t * Group fields works the same, except ids only need\n\t * to be unique to the group. Prefix is not needed.\n\t *\n\t * The parent field's id needs to be passed as the second argument.\n\t */\n\t$cmb_group->add_group_field( $group_field_id, array(\n\t\t'name' => 'Entry Title',\n\t\t'id'   => 'title',\n\t\t'type' => 'text',\n\t) );\n\n\t$cmb_group->add_group_field( $group_field_id, array(\n\t\t'name'        => 'Description',\n\t\t'description' => 'Write a short description for this entry',\n\t\t'id'          => 'description',\n\t\t'type'        => 'wysiwyg',\n\t\t'options'     => array(\n\t\t\t'textarea_rows' => 3,\n\t\t),\n\t) );\n};\n\nadd_filter( 'cmb2_override_yourprefix_group_alt_data_demo_meta_value', 'yourprefix_group_alt_data_demo_override_meta_value', 10, 4 );\nfunction yourprefix_group_alt_data_demo_override_meta_value( $data, $object_id, $args, $field ) {\n\n\t// Here, we're pulling from the options table, but you can query from any data source here.\n\t// If from a custom table, you can use the $object_id to query against.\n\treturn get_option( 'yourprefix_group_alt_data_demo', array() );\n}\n\nadd_filter( 'cmb2_override_yourprefix_group_alt_data_demo_meta_save', 'yourprefix_group_alt_data_demo_override_meta_save', 10, 4 );\nfunction yourprefix_group_alt_data_demo_override_meta_save( $override, $args, $field_args, $field ) {\n\n\t// Here, we're storing the data to the options table, but you can store to any data source here.\n\t// If to a custom table, you can use the $args['id'] as the reference id.\n\t$updated = update_option( 'yourprefix_group_alt_data_demo', $args['value'] );\n\treturn !! $updated;\n}\n\nadd_filter( 'cmb2_override_yourprefix_group_alt_data_demo_meta_remove', 'yourprefix_group_alt_data_demo_override_meta_remove', 10, 4 );\nfunction yourprefix_group_alt_data_demo_override_meta_remove( $override, $args, $field_args, $field ) {\n\n\t// Here, we're removing from the options table, but you can query to remove from any data source here.\n\t// If from a custom table, you can use the $args['id'] to query against.\n\t// (If we do \"delete_option\", then our default value will be re-applied, which isn't desired.)\n\t$updated = update_option( 'yourprefix_group_alt_data_demo', array() );\n\treturn !! $updated;\n}\n"
  },
  {
    "path": "filters-and-actions/save-default-group-field-value-based-on-other-field.php",
    "content": "<?php\n\n/**\n * The following snippet is untested, but provides a proof of concept to how you can save a default value for a group field based on the value of another field.\n */\n\n// Setup a group field... the group field is proceeded by a radio field.\nfunction yourprefix_register_repeatable_group_field_metabox() {\n\t/**\n\t * Repeatable Field Groups\n\t */\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_group_metabox',\n\t\t'title'        => __( 'Repeating Field Group', 'cmb2' ),\n\t\t'object_types' => array( 'page', ),\n\t) );\n\n\t$cmb->add_field( $group_field_id, array(\n\t\t'name'             => __( 'Radio', 'cmb2' ),\n\t\t'id'               => 'yourprefix_group_radio',\n\t\t'type'             => 'radio',\n\t\t'options' => array(\n\t\t\t'standard' => __( 'Option One', 'cmb2' ),\n\t\t\t'custom'   => __( 'Option Two', 'cmb2' ),\n\t\t\t'none'     => __( 'Option Three', 'cmb2' ),\n\t\t),\n\t) );\n\n\t$group_field_id = $cmb->add_field( array(\n\t\t'id'          => 'yourprefix_group_demo',\n\t\t'type'        => 'group',\n\t\t'description' => __( 'Generates reusable form entries', 'cmb2' ),\n\t\t'options'     => array(\n\t\t\t'group_title'    => __( 'Entry {#}', 'cmb2' ),\n\t\t\t'add_button'     => __( 'Add Another Entry', 'cmb2' ),\n\t\t\t'remove_button'  => __( 'Remove Entry', 'cmb2' ),\n\t\t\t'sortable'       => true,\n\t\t),\n\t) );\n\n\t$cmb->add_group_field( $group_field_id, array(\n\t\t'name' => 'Entry Title',\n\t\t'id'   => 'title',\n\t\t'type' => 'text',\n\t) );\n\n\t$cmb->add_group_field( $group_field_id, array(\n\t\t'name'        => 'Description',\n\t\t'description' => 'Write a short description for this entry',\n\t\t'id'          => 'description',\n\t\t'type'        => 'textarea_small',\n\t) );\n\n}\nadd_action( 'cmb2_init', 'yourprefix_register_repeatable_group_field_metabox' );\n\n\n// If the radio field is set to 'standard', then update the group field value to have one group filled-in.\nfunction hook_in_and_add_default_group_value( $post_id, $updated, $cmb ) {\n\t// If 'my_meta_key' was updated, then proceed w/ my stuff.\n\tif ( in_array( 'yourprefix_group_radio', $updated ) ) {\n\t\tif ( 'standard' === get_post_meta( $post_id, 'yourprefix_group_radio', 1 ) ) {\n\t\t\t// do stuff\n\t\t\tupdate_post_meta( $post_id, 'yourprefix_group_demo', array(\n\t\t\t\tarray(\n\t\t\t\t\t'title'       => 'Title of group 1',\n\t\t\t\t\t'description' => 'Description of group 1',\n\t\t\t\t)\n\t\t\t) );\n\t\t}\n\t}\n}\nadd_action( 'cmb2_save_post_fields_yourprefix_group_metabox', 'hook_in_and_add_default_group_value', 10, 3 );\n"
  },
  {
    "path": "front-end/README.md",
    "content": "Front-end\n==========\n\nSnippets that use CMB2 on the front-end (not wp-admin) side of your site."
  },
  {
    "path": "front-end/cmb2-front-end-editor.php",
    "content": "<?php\n/*\n * Frontend editor, including title/content\n */\n\nfunction jt_add_edit_form_to_frontend( $content ) {\n\tif ( isset( $_GET['edit'] ) ) {\n\t\tif ( current_user_can( 'edit_posts' ) && wp_verify_nonce( $_GET['edit'], 'edit' ) ) {\n\t\t\t$content = cmb2_get_metabox_form( '_yourprefix_demo_metabox', get_the_ID() );\n\t\t} else {\n\t\t\t$content = '<h2 class=\"edit-error\">You do not have permission to edit this post.</h2>';\n\t\t}\n\t}\n\n\treturn $content;\n}\nadd_filter( 'the_content', 'jt_add_edit_form_to_frontend' );\n\n/**\n * Modify the edit links to point to the front-end editor.\n */\nfunction jt_modify_edit_link( $link ) {\n\tif ( ! is_admin() ) {\n\t\t$link = esc_url_raw( wp_nonce_url( remove_query_arg( 'edit' ), 'edit', 'edit' ) );\n\t}\n\treturn $link;\n}\nadd_filter( 'get_edit_post_link', 'jt_modify_edit_link' );\n\n/**\n * Hook in later and prepend our title/content fields to our existing metabox\n */\nfunction jt_edit_core_fields() {\n\tif ( ! is_admin() ) { // only if on front-end\n\n\t\t// Get existing metabox\n\t\t$cmb = cmb2_get_metabox( '_yourprefix_demo_metabox' );\n\n\t\t// and prepend title\n\t\t$cmb->add_field( array(\n\t\t\t'name'   => __( 'Title', 'cmb2' ),\n\t\t\t'id'     => 'post_title',\n\t\t\t'type'   => 'text',\n\t\t\t'before' => 'jt_edit_core_maybe_redirect',\n\t\t), 1 );\n\n\t\t// and content fields\n\t\t$cmb->add_field( array(\n\t\t\t'name'  => __( 'Content', 'cmb2' ),\n\t\t\t'id'    => 'post_content',\n\t\t\t'type'  => 'wysiwyg',\n\t\t), 2 );\n\t}\n}\nadd_action( 'cmb2_init', 'jt_edit_core_fields', 99 );\n\n/**\n * If edit was saved, redirect to non-edit page\n */\nfunction jt_edit_core_maybe_redirect() {\n\tif ( isset( $_POST['post_content'] ) ) {\n\t\t$url = esc_url_raw( remove_query_arg( 'edit' ) );\n\t\techo \"<script type='text/javascript'>window.location.href = '$url';</script>\";\n\t}\n}\n\n/**\n * We don't want CMB2 to fetch data from meta for post title and post content\n */\nfunction jt_cmb2_override_core_field_get( $val, $object_id, $a, $field ) {\n\tglobal $post;\n\n\tif ( in_array( $field->id(), array( 'post_title', 'post_content' ), true ) ) {\n\t\tif ( isset( $post->ID ) ) {\n\t\t\t$val = get_post_field( $field->id(), $post );\n\t\t} else {\n\t\t\t$val = '';\n\t\t}\n\t}\n\n\treturn $val;\n}\nadd_filter( 'cmb2_override_meta_value', 'jt_cmb2_override_core_field_get', 10, 4 );\n\n/**\n * We don't want CMB2 to save data to meta for post title and post content\n */\nfunction jt_cmb2_override_core_field_set( $status, $a, $args, $field ) {\n\tglobal $post;\n\n\tif ( in_array( $field->id(), array( 'post_title', 'post_content' ), true ) ) {\n\t\tif ( isset( $post->ID ) ) {\n\t\t\t$status = wp_update_post( array(\n\t\t\t\t$field->id() => $a['value'],\n\t\t\t\t'ID' => $post->ID,\n\t\t\t) );\n\n\t\t} else {\n\t\t\t$status = false;\n\t\t}\n\t}\n\n\treturn $status;\n}\nadd_filter( 'cmb2_override_meta_save', 'jt_cmb2_override_core_field_set', 10, 4 );\n"
  },
  {
    "path": "front-end/cmb2-front-end-submit.php",
    "content": "<?php\n/**\n * @link http://webdevstudios.com/2015/03/30/use-cmb2-to-create-a-new-post-submission-form/ Original tutorial\n */\n\n\n/**\n * Register the form and fields for our front-end submission form\n */\nfunction yourprefix_frontend_form_register() {\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'front-end-post-form',\n\t\t'object_types' => array( 'post' ),\n\t\t'hookup'       => false,\n\t\t'save_fields'  => false,\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name'    => __( 'New Post Title', 'YOURTEXTDOMAIN' ),\n\t\t'id'      => 'submitted_post_title',\n\t\t'type'    => 'text',\n\t\t'default' => ! empty( $_POST['submitted_post_title'] )\n\t\t\t? $_POST['submitted_post_title']\n\t\t\t: __( 'New Post', 'YOURTEXTDOMAIN' ),\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'default_cb' => 'yourprefix_maybe_set_default_from_posted_values',\n\t\t'name'       => __( 'New Post Content', 'YOURTEXTDOMAIN' ),\n\t\t'id'         => 'submitted_post_content',\n\t\t'type'       => 'wysiwyg',\n\t\t'options'    => array(\n\t\t\t'textarea_rows' => 12,\n\t\t\t'media_buttons' => false,\n\t\t),\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'default_cb' => 'yourprefix_maybe_set_default_from_posted_values',\n\t\t'name'       => __( 'Featured Image for New Post', 'YOURTEXTDOMAIN' ),\n\t\t'id'         => 'submitted_post_thumbnail',\n\t\t'type'       => 'text',\n\t\t'attributes' => array(\n\t\t\t'type' => 'file', // Let's use a standard file upload field\n\t\t),\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'default_cb' => 'yourprefix_maybe_set_default_from_posted_values',\n\t\t'name'       => __( 'Categories', 'YOURTEXTDOMAIN' ),\n\t\t'id'         => 'submitted_categories',\n\t\t'type'       => 'taxonomy_multicheck',\n\t\t'taxonomy'   => 'category', // Taxonomy Slug\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'default_cb' => 'yourprefix_maybe_set_default_from_posted_values',\n\t\t'name'       => __( 'Your Name', 'YOURTEXTDOMAIN' ),\n\t\t'desc'       => __( 'Please enter your name for author credit on the new post.', 'YOURTEXTDOMAIN' ),\n\t\t'id'         => 'submitted_author_name',\n\t\t'type'       => 'text',\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'default_cb' => 'yourprefix_maybe_set_default_from_posted_values',\n\t\t'name'       => __( 'Your Email', 'YOURTEXTDOMAIN' ),\n\t\t'desc'       => __( 'Please enter your email so we can contact you if we use your post.', 'YOURTEXTDOMAIN' ),\n\t\t'id'         => 'submitted_author_email',\n\t\t'type'       => 'text_email',\n\t) );\n\n}\nadd_action( 'cmb2_init', 'yourprefix_frontend_form_register' );\n\n/**\n * Sets the front-end-post-form field values if form has already been submitted.\n *\n * @return string\n */\nfunction yourprefix_maybe_set_default_from_posted_values( $args, $field ) {\n\tif ( ! empty( $_POST[ $field->id() ] ) ) {\n\t\treturn $_POST[ $field->id() ];\n\t}\n\n\treturn '';\n}\n\n/**\n * Gets the front-end-post-form cmb instance\n *\n * @return CMB2 object\n */\nfunction yourprefix_frontend_cmb2_get() {\n\t// Use ID of metabox in yourprefix_frontend_form_register\n\t$metabox_id = 'front-end-post-form';\n\n\t// Post/object ID is not applicable since we're using this form for submission\n\t$object_id  = 'fake-oject-id';\n\n\t// Get CMB2 metabox object\n\treturn cmb2_get_metabox( $metabox_id, $object_id );\n}\n\n/**\n * Handle the cmb_frontend_form shortcode\n *\n * @param  array  $atts Array of shortcode attributes\n * @return string       Form html\n */\nfunction yourprefix_do_frontend_form_submission_shortcode( $atts = array() ) {\n\n\t// Get CMB2 metabox object\n\t$cmb = yourprefix_frontend_cmb2_get();\n\n\t// Get $cmb object_types\n\t$post_types = $cmb->prop( 'object_types' );\n\n\t// Current user\n\t$user_id = get_current_user_id();\n\n\t// Parse attributes\n\t$atts = shortcode_atts( array(\n\t\t'post_author' => $user_id ? $user_id : 1, // Current user, or admin\n\t\t'post_status' => 'pending',\n\t\t'post_type'   => reset( $post_types ), // Only use first object_type in array\n\t), $atts, 'cmb_frontend_form' );\n\n\t/*\n\t * Let's add these attributes as hidden fields to our cmb form\n\t * so that they will be passed through to our form submission\n\t */\n\tforeach ( $atts as $key => $value ) {\n\t\t$cmb->add_hidden_field( array(\n\t\t\t'field_args'  => array(\n\t\t\t\t'id'    => \"atts[$key]\",\n\t\t\t\t'type'  => 'hidden',\n\t\t\t\t'default' => $value,\n\t\t\t),\n\t\t) );\n\t}\n\n\t// Initiate our output variable\n\t$output = '';\n\n\t// Get any submission errors\n\tif ( ( $error = $cmb->prop( 'submission_error' ) ) && is_wp_error( $error ) ) {\n\t\t// If there was an error with the submission, add it to our ouput.\n\t\t$output .= '<h3>' . sprintf( __( 'There was an error in the submission: %s', 'YOURTEXTDOMAIN' ), '<strong>'. $error->get_error_message() .'</strong>' ) . '</h3>';\n\t}\n\n\t// If the post was submitted successfully, notify the user.\n\tif ( isset( $_GET['post_submitted'] ) && ( $post = get_post( absint( $_GET['post_submitted'] ) ) ) ) {\n\n\t\t// Get submitter's name\n\t\t$name = get_post_meta( $post->ID, 'submitted_author_name', 1 );\n\t\t$name = $name ? ' '. $name : '';\n\n\t\t// Add notice of submission to our output\n\t\t$output .= '<h3>' . sprintf( __( 'Thank you%s, your new post has been submitted and is pending review by a site administrator.', 'YOURTEXTDOMAIN' ), esc_html( $name ) ) . '</h3>';\n\t}\n\n\t// Get our form\n\t$output .= cmb2_get_metabox_form( $cmb, 'fake-oject-id', array( 'save_button' => __( 'Submit Post', 'YOURTEXTDOMAIN' ) ) );\n\n\treturn $output;\n}\nadd_shortcode( 'cmb_frontend_form', 'yourprefix_do_frontend_form_submission_shortcode' );\n\n/**\n * Handles form submission on save. Redirects if save is successful, otherwise sets an error message as a cmb property\n *\n * @return void\n */\nfunction yourprefix_handle_frontend_new_post_form_submission() {\n\n\t// If no form submission, bail\n\tif ( empty( $_POST ) || ! isset( $_POST['submit-cmb'], $_POST['object_id'] ) ) {\n\t\treturn false;\n\t}\n\n\t// Get CMB2 metabox object\n\t$cmb = yourprefix_frontend_cmb2_get();\n\n\t$post_data = array();\n\n\t// Get our shortcode attributes and set them as our initial post_data args\n\tif ( isset( $_POST['atts'] ) ) {\n\t\tforeach ( (array) $_POST['atts'] as $key => $value ) {\n\t\t\t$post_data[ $key ] = sanitize_text_field( $value );\n\t\t}\n\t\tunset( $_POST['atts'] );\n\t}\n\n\t// Check security nonce\n\tif ( ! isset( $_POST[ $cmb->nonce() ] ) || ! wp_verify_nonce( $_POST[ $cmb->nonce() ], $cmb->nonce() ) ) {\n\t\treturn $cmb->prop( 'submission_error', new WP_Error( 'security_fail', __( 'Security check failed.' ) ) );\n\t}\n\n\t// Check title submitted\n\tif ( empty( $_POST['submitted_post_title'] ) ) {\n\t\treturn $cmb->prop( 'submission_error', new WP_Error( 'post_data_missing', __( 'New post requires a title.' ) ) );\n\t}\n\n\t// And that the title is not the default title\n\tif ( $cmb->get_field( 'submitted_post_title' )->default() == $_POST['submitted_post_title'] ) {\n\t\treturn $cmb->prop( 'submission_error', new WP_Error( 'post_data_missing', __( 'Please enter a new title.' ) ) );\n\t}\n\n\t/**\n\t * Fetch sanitized values\n\t */\n\t$sanitized_values = $cmb->get_sanitized_values( $_POST );\n\n\t// Set our post data arguments\n\t$post_data['post_title']   = $sanitized_values['submitted_post_title'];\n\tunset( $sanitized_values['submitted_post_title'] );\n\t$post_data['post_content'] = $sanitized_values['submitted_post_content'];\n\tunset( $sanitized_values['submitted_post_content'] );\n\n\t// Create the new post\n\t$new_submission_id = wp_insert_post( $post_data, true );\n\n\t// If we hit a snag, update the user\n\tif ( is_wp_error( $new_submission_id ) ) {\n\t\treturn $cmb->prop( 'submission_error', $new_submission_id );\n\t}\n\n\t$cmb->save_fields( $new_submission_id, 'post', $sanitized_values );\n\n\t/**\n\t * Other than post_type and post_status, we want\n\t * our uploaded attachment post to have the same post-data\n\t */\n\tunset( $post_data['post_type'] );\n\tunset( $post_data['post_status'] );\n\n\t// Try to upload the featured image\n\t$img_id = yourprefix_frontend_form_photo_upload( $new_submission_id, $post_data );\n\n\t// If our photo upload was successful, set the featured image\n\tif ( $img_id && ! is_wp_error( $img_id ) ) {\n\t\tset_post_thumbnail( $new_submission_id, $img_id );\n\t}\n\n\t/*\n\t * Redirect back to the form page with a query variable with the new post ID.\n\t * This will help double-submissions with browser refreshes\n\t */\n\twp_redirect( esc_url_raw( add_query_arg( 'post_submitted', $new_submission_id ) ) );\n\texit;\n}\nadd_action( 'cmb2_after_init', 'yourprefix_handle_frontend_new_post_form_submission' );\n\n/**\n * Handles uploading a file to a WordPress post\n *\n * @param  int   $post_id              Post ID to upload the photo to\n * @param  array $attachment_post_data Attachement post-data array\n */\nfunction yourprefix_frontend_form_photo_upload( $post_id, $attachment_post_data = array() ) {\n\t// Make sure the right files were submitted\n\tif (\n\t\tempty( $_FILES )\n\t\t|| ! isset( $_FILES['submitted_post_thumbnail'] )\n\t\t|| isset( $_FILES['submitted_post_thumbnail']['error'] ) && 0 !== $_FILES['submitted_post_thumbnail']['error']\n\t) {\n\t\treturn;\n\t}\n\n\t// Filter out empty array values\n\t$files = array_filter( $_FILES['submitted_post_thumbnail'] );\n\n\t// Make sure files were submitted at all\n\tif ( empty( $files ) ) {\n\t\treturn;\n\t}\n\n\t// Make sure to include the WordPress media uploader API if it's not (front-end)\n\tif ( ! function_exists( 'media_handle_upload' ) ) {\n\t\trequire_once( ABSPATH . 'wp-admin/includes/image.php' );\n\t\trequire_once( ABSPATH . 'wp-admin/includes/file.php' );\n\t\trequire_once( ABSPATH . 'wp-admin/includes/media.php' );\n\t}\n\n\t// Upload the file and send back the attachment post ID\n\treturn media_handle_upload( 'submitted_post_thumbnail', $post_id, $attachment_post_data );\n}\n"
  },
  {
    "path": "front-end/cmb2-front-end-wordpress-media-uploader.php",
    "content": "<?php\n/**\n * Use the WordPress Media Uploader on the frontend. Limit to only displaying current\n * user's uploaded media. Props [@vasikgreif](https://github.com/vasikgreif)\n *\n * @link https://github.com/WebDevStudios/CMB2/issues/283 Original concept\n */\n\n/**\n * Allow the media uploader work on specific pages\n */\nfunction nevestam_allow_contributor_uploads() {\n\tif ( is_admin() ) {\n\t\treturn;\n\t}\n\n\t$path = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '';\n\n\tif ( ! $path || '/page-slug' != $path ) {\n\t\treturn;\n\t}\n\n\t/**\n\t * Replace 'subscriber' with the required role to update, can also be contributor\n\t */\n\t$subscriber = get_role( 'subscriber' );\n\n\t// This is the only cap needed to upload files.\n\t$subscriber->add_cap( 'upload_files' );\n\n}\n\nadd_action( 'init', 'nevestam_allow_contributor_uploads' );\n\n/**\n * Display only user-uploaded files to each user\n *\n * @param WP_Query $wp_query_obj\n */\nfunction nevestam_restrict_media_library( $wp_query_obj ) {\n\tglobal $current_user, $pagenow;\n\n\tif ( ! is_a( $current_user, 'WP_User' ) ) {\n\t\treturn;\n\t}\n\n\tif ( 'admin-ajax.php' != $pagenow || 'query-attachments' != $_REQUEST['action'] ) {\n\t\treturn;\n\t}\n\n\tif ( ! current_user_can( 'manage_media_library' ) ) {\n\t\t$wp_query_obj->set( 'author', $current_user->ID );\n\t}\n}\n\nadd_action( 'pre_get_posts', 'nevestam_restrict_media_library' );\n"
  },
  {
    "path": "front-end/cmb2-metabox-shortcode.php",
    "content": "<?php\n/*\n * Plugin Name: CMB2 - Front-end Shortcode\n * Description: Display CMB2 metabox forms on the front-end using a shortcode\n * Author: jtsternberg\n * Author URI: http://dsgnwrks.pro\n * Version: 0.1.0\n */\n\n/**\n * Shortcode to display a CMB2 form for a post ID.\n * Adding this shortcode to your WordPress editor would look something like this:\n *\n * [cmb-form id=\"test_metabox\" post_id=2]\n *\n * The shortcode requires a metabox ID, and (optionally) can take\n * a WordPress post ID (or user/comment ID) to be editing.\n *\n * @param  array  $atts Shortcode attributes\n * @return string       Form HTML markup\n */\nfunction jt_cmb2_do_frontend_form_shortcode( $atts = array() ) {\n    global $post;\n\n    /**\n     * Make sure a WordPress post ID is set.\n     * We'll default to the current post/page\n     */\n    if ( ! isset( $atts['post_id'] ) ) {\n        $atts['post_id'] = $post->ID;\n    }\n\n    // If no metabox id is set, yell about it\n    if ( empty( $atts['id'] ) ) {\n        return 'Please add an \"id\" attribute to specify the CMB2 form to display.';\n    }\n\n    $metabox_id = esc_attr( $atts['id'] );\n    $object_id = absint( $atts['post_id'] );\n    // Get our form\n    $form = cmb2_get_metabox_form( $metabox_id, $object_id );\n\n    return $form;\n}\nadd_shortcode( 'cmb-form', 'jt_cmb2_do_frontend_form_shortcode' );\n"
  },
  {
    "path": "front-end/output-file-list.php",
    "content": "<?php\n/*\n * Plugin Name: CMB2 Template Tag - Output File List\n * Description: Custom functions that allow you to get or ouput the file_list images in a specified size\n * Author: jtsternberg\n * Author URI: http://dsgnwrks.pro\n * Version: 0.1.0\n */\n\n/**\n * Outputs a cmb2 file_list\n *\n * @param  string  $file_list_meta_key The field meta key. ($prefix . 'file_list')\n * @param  string  $img_size           Size of image to show\n */\nfunction jt_cmb2_file_list_images( $file_list_meta_key, $img_size = 'medium' ) {\n\techo jt_cmb2_get_file_list_images( $file_list_meta_key, $img_size );\n}\n\n/**\n * Returns a cmb2 file_list\n *\n * @param  string  $file_list_meta_key The field meta key. ($prefix . 'file_list')\n * @param  string  $img_size           Size of image to show\n * @return string                      The html markup for the images\n */\nfunction jt_cmb2_get_file_list_images( $file_list_meta_key, $img_size = 'medium' ) {\n\n\t// Get the list of files\n\t$files = get_post_meta( get_the_ID(), $file_list_meta_key, 1 );\n\t$images = '';\n\t// Loop through them and output an image\n\tforeach ( (array) $files as $attachment_id => $attachment_url ) {\n\t\t$images .= '<div class=\"file-list-image\">';\n\t\t$images .= wp_get_attachment_image( $attachment_id, $img_size );\n\t\t$images .= '</div>';\n\t}\n\n\treturn $images ? '<div class=\"file-list-wrap\">' . $images . '</div>' : '';\n}\n"
  },
  {
    "path": "helper-functions/README.md",
    "content": "CMB2 Helper Functions\n==========\n\nSnippets for working with the [included helper functions](https://github.com/WebDevStudios/CMB2/blob/master/includes/helper-functions.php).\n\nRelated CMB2 issues:\n* [#130](https://github.com/WebDevStudios/CMB2/issues/130#issuecomment-68160722)\n"
  },
  {
    "path": "helper-functions/helper-functions.php",
    "content": "<?php\n/**\n * Just a collection of random helper functions for CMB2.\n * If you borrow these, please replace \"yourprefix_\" with something unique to your project.\n */\n\n/**\n * If it exists, handles uploading a file from the $_FILES array, and setting data to CMB2 file field.\n *\n * @param  string $files_key Array key in $_FILES array.\n * @param  int    $post_id   Post ID\n * @param  string $meta_key  The CMB2 'file' field ID.\n * @param  string $title     Override the default file title handling.\n *\n * @return array|WP_Error\n */\nfunction yourprefix_upload_file_for_cmb2( $files_key, $post_id, $meta_key = '', $title = '' ) {\n\tif (\n\t\tempty( $_FILES[ $files_key ] )\n\t\t|| isset( $_FILES[ $files_key ]['error'] ) && 0 !== $_FILES[ $files_key ]['error']\n\t) {\n\t\treturn false;\n\t}\n\n\trequire_once( ABSPATH . 'wp-admin/includes/media.php' );\n\trequire_once( ABSPATH . 'wp-admin/includes/file.php' );\n\trequire_once( ABSPATH . 'wp-admin/includes/image.php' );\n\n\t$post_data = array();\n\n\tif ( $title ) {\n\t\t$post_data['post_title'] = get_the_title( $post_id ) . ': Flyer';\n\t}\n\n\t$id = media_handle_upload( $files_key, $post_id, $post_dataarray );\n\n\tif ( is_wp_error( $id ) ) {\n\t\treturn $id;\n\t}\n\n\t$src = wp_get_attachment_url( $id );\n\n\tif ( $meta_key ) {\n\t\tupdate_post_meta( $post_id, $meta_key, $src );\n\t\tupdate_post_meta( $post_id, \"{$meta_key}_id\", $id );\n\t}\n\n\treturn compact( 'src', 'id' );\n}\n\n\n/**\n * Handles sideloading a file from given $file_url, and setting data to CMB2 file field.\n *\n * @param  string $file_url URL to file to sideload.\n * @param  int    $post_id  Post ID\n * @param  string $meta_key The CMB2 'file' field ID.\n * @param  string $title    Override the default file title handling.\n *\n * @return array|WP_Error\n */\nfunction yourprefix_sideload_file_for_cmb2( $file_url, $post_id, $meta_key, = '', $title = null ) {\n\trequire_once( ABSPATH . 'wp-admin/includes/media.php' );\n\trequire_once( ABSPATH . 'wp-admin/includes/file.php' );\n\trequire_once( ABSPATH . 'wp-admin/includes/image.php' );\n\n\t$parts = explode( '/', $file_url );\n\n\t$file_array = array();\n\t$file_array['name'] = end( $parts );\n\n\t// Download file to temp location.\n\t$file_array['tmp_name'] = download_url( $file_url );\n\n\t// If error storing temporarily, return the error.\n\tif ( is_wp_error( $file_array['tmp_name'] ) ) {\n\t\treturn $file_array['tmp_name'];\n\t}\n\n\t// Do the validation and storage stuff.\n\t$id = media_handle_sideload( $file_array, $post_id, $title );\n\n\t// If error storing permanently, unlink.\n\tif ( is_wp_error( $id ) ) {\n\t\t@unlink( $file_array['tmp_name'] );\n\t\treturn $id;\n\t}\n\n\t$src = wp_get_attachment_url( $id );\n\n\tif ( $meta_key ) {\n\t\tupdate_post_meta( $post_id, $meta_key, $src );\n\t\tupdate_post_meta( $post_id, \"{$meta_key}_id\", $id );\n\t}\n\n\treturn compact( 'src', 'id' );\n}\n\n/**\n * If you want oembed filters to apply to the wysiwyg content, add this helper function to your theme or plugin.\n *\n * @link  https://github.com/WebDevStudios/CMB2/wiki/Field-Types#notes-2 Wiki\n *\n * @param  string  $meta_key The CMB2 text field (e.g. 'wysiwyg', 'textarea', etc) ID.\n * @param  int     $post_id  Post ID\n *\n * @return string            If the field has a value, returns the formatted value;\n */\nfunction yourprefix_get_cmb2_wysiwyg_output( $meta_key, $post_id = 0 ) {\n\tglobal $wp_embed;\n\n\t$post_id = $post_id ? $post_id : get_the_id();\n\n\t$content = get_post_meta( $post_id, $meta_key, 1 );\n\tif ( ! $content ) {\n\t\treturn '';\n\t}\n\n\t$content = $wp_embed->autoembed( $content );\n\t$content = $wp_embed->run_shortcode( $content );\n\t$content = wpautop( $content );\n\t$content = do_shortcode( $content );\n\n\treturn $content;\n}\n\n/**\n * Sample template tag function for outputting a cmb2 file_list.\n *\n * @link  https://github.com/WebDevStudios/CMB2/wiki/Field-Types#sample-function-for-getting-and-outputting-file_list-images Wiki\n *\n * @param  string  $meta_key The 'file_list' field meta key.\n * @param  string  $img_size Size of image to display.\n */\nfunction yourprefix_cmb2_output_file_list( $meta_key, $img_size = 'medium' ) {\n\n\t// Get the list of files\n\t$files = get_post_meta( get_the_ID(), $meta_key, 1 );\n\n\techo '<div class=\"file-list-wrap\">';\n\t// Loop through them and output an image\n\tforeach ( (array) $files as $attachment_id => $attachment_url ) {\n\t\techo '<div class=\"file-list-image\">';\n\t\techo wp_get_attachment_image( $attachment_id, $img_size );\n\t\techo '</div>';\n\t}\n\techo '</div>';\n}\n"
  },
  {
    "path": "helper-functions/modify-cmb2_metabox_form-format.php",
    "content": "<?php\n\n/**\n * To change the formatting of the form,\n * again, pass the third parameter (the arguments array),\n * and specify the 'form_format' param\n *\n * In our case, we're modifying the save button text,\n * and we're giving the save button a secondary button class\n */\ncmb2_metabox_form( $meta_box, $object_id, array(\n    'form_format' => '<form class=\"cmb-form\" method=\"post\" id=\"%1$s\" enctype=\"multipart/form-data\" encoding=\"multipart/form-data\"><input type=\"hidden\" name=\"object_id\" value=\"%2$s\">%3$s<div class=\"submit-wrap\"><input type=\"submit\" name=\"submit-cmb\" value=\"' . __( 'Save Settings', 'your-textdomain' ) . '\" class=\"button-secondary\"></div></form>',\n) );\n"
  },
  {
    "path": "helper-functions/modify-cmb2_metabox_form-output.php",
    "content": "<?php\n/**\n * You can use the 'cmb2_get_metabox_form_format' filter to modify the form format,\n * but keep in mind it will affect the form format for all uses of\n * the cmb2_metabox_form, cmb2_print_metabox_form, and cmb2_get_metabox_form functions,\n * so you should make sure you're looking at the right form.\n *\n * If you're using the options-page example,\n * (https://github.com/WebDevStudios/CMB2-Snippet-Library/tree/master/options-and-settings-pages)\n * You would do it like this:\n */\n\n/**\n * Modify CMB2 Default Form Output\n *\n * @param  string  $form_format Form output format\n * @param  string  $object_id   In the case of an options page, this will be the option key\n * @param  object  $cmb         CMB2 object. Can use $cmb->cmb_id to retrieve the metabox ID\n *\n * @return string               Possibly modified form output\n */\nfunction myprefix_options_modify_cmb2_metabox_form_format( $form_format, $object_id, $cmb ) {\n\n    if ( 'myprefix_options' == $object_id && 'option_metabox' == $cmb->cmb_id ) {\n\n        $form_format = '<form class=\"cmb-form\" method=\"post\" id=\"%1$s\" enctype=\"multipart/form-data\" encoding=\"multipart/form-data\"><input type=\"hidden\" name=\"object_id\" value=\"%2$s\">%3$s<div class=\"submit-wrap\"><input type=\"submit\" name=\"submit-cmb\" value=\"' . __( 'Save Settings', 'myprefix' ) . '\" class=\"button-primary\"></div></form>';\n    }\n\n    return $form_format;\n}\nadd_filter( 'cmb2_get_metabox_form_format', 'myprefix_options_modify_cmb2_metabox_form_format', 10, 3 );\n"
  },
  {
    "path": "helper-functions/modify-cmb2_metabox_form-save-button-text.php",
    "content": "<?php\n\n/**\n * To change just the text of the save button,\n * pass the third parameter (the arguments array),\n * and specify the 'save_button' param\n */\ncmb2_metabox_form( $meta_box, $object_id, array(\n\t'save_button' => __( 'Save Settings', 'your-textdomain' ),\n) );\n"
  },
  {
    "path": "javascript/README.md",
    "content": "CMB2 Javascript\n==========\n\nCustom javascript snippets for interacting with CMB2."
  },
  {
    "path": "javascript/cmb2-auto-scroll-to-new-group.php",
    "content": "<?php\n/*\n * Plugin Name: CMB2 Auto-scroll to new group\n * Description: This feature was removed in CMB2 2.0.3. Install this plugin to re-activate.\n * Author: jtsternberg\n * Author URI: http://dsgnwrks.pro\n * Version: 0.1.0\n */\n\nfunction jt_cmb_group_autoscroll_js() {\n\n\t// If not cmb2 scripts on this page, bail\n\tif ( ! wp_script_is( 'cmb2-scripts', 'enqueued' ) ) {\n\t\treturn;\n\t}\n\n\t?>\n\t<script type=\"text/javascript\">\n\t\twindow.CMB2 = window.CMB2 || {};\n\t\t(function(window, document, $, cmb, undefined){\n\t\t\t'use strict';\n\n\t\t\t// We'll keep it in the CMB2 object namespace\n\t\t\tcmb.initAutoScrollGroup = function(){\n\t\t\t\tcmb.metabox().find('.cmb-repeatable-group').on( 'cmb2_add_row', cmb.autoScrollGroup );\n\t\t\t};\n\n\t\t\tcmb.autoScrollGroup = function( evt, row ) {\n\t\t\t\tvar $focus = $(row).find('input:not([type=\"button\"]), textarea, select').first();\n\t\t\t\tif ( $focus.length ) {\n\t\t\t\t\t$( 'html, body' ).animate({\n\t\t\t\t\t\tscrollTop: Math.round( $focus.offset().top - 150 )\n\t\t\t\t\t}, 1000);\n\t\t\t\t\t$focus.focus();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t$(document).ready( cmb.initAutoScrollGroup );\n\n\t\t})(window, document, jQuery, CMB2);\n\t</script>\n\t<?php\n}\nadd_action( 'admin_footer', 'jt_cmb_group_autoscroll_js', 999 );\n"
  },
  {
    "path": "javascript/cmb2-js-validation-required.php",
    "content": "<?php\n/*\n * Plugin Name: CMB2 js validation for \"required\" fields\n * Description: Uses js to validate CMB2 fields that have the 'data-validation' attribute set to 'required'\n * Version: 0.1.0\n */\n\n/**\n * Documentation in the wiki:\n * @link https://github.com/WebDevStudios/CMB2/wiki/Plugin-code-to-add-JS-validation-of-%22required%22-fields\n */\n\nfunction cmb2_after_form_do_js_validation( $post_id, $cmb ) {\n\tstatic $added = false;\n\n\t// Only add this to the page once (not for every metabox)\n\tif ( $added ) {\n\t\treturn;\n\t}\n\n\t$added = true;\n\t?>\n\t<script type=\"text/javascript\">\n\tjQuery(document).ready(function($) {\n\n\t\t$form = $( document.getElementById( 'post' ) );\n\t\t$htmlbody = $( 'html, body' );\n\t\t$toValidate = $( '[data-validation]' );\n\n\t\tif ( ! $toValidate.length ) {\n\t\t\treturn;\n\t\t}\n\n\t\tfunction checkValidation( evt ) {\n\t\t\tvar labels = [];\n\t\t\tvar $first_error_row = null;\n\t\t\tvar $row = null;\n\n\t\t\tfunction add_required( $row ) {\n\t\t\t\t$row.css({ 'background-color': 'rgb(255, 170, 170)' });\n\t\t\t\t$first_error_row = $first_error_row ? $first_error_row : $row;\n\t\t\t\tlabels.push( $row.find( '.cmb-th label' ).text() );\n\t\t\t}\n\n\t\t\tfunction remove_required( $row ) {\n\t\t\t\t$row.css({ background: '' });\n\t\t\t}\n\n\t\t\t$toValidate.each( function() {\n\t\t\t\tvar $this = $(this);\n\t\t\t\tvar val = $this.val();\n\t\t\t\t$row = $this.parents( '.cmb-row' );\n\n\t\t\t\tif ( $this.is( '[type=\"button\"]' ) || $this.is( '.cmb2-upload-file-id' ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tif ( 'required' === $this.data( 'validation' ) ) {\n\t\t\t\t\tif ( $row.is( '.cmb-type-file-list' ) ) {\n\n\t\t\t\t\t\tvar has_LIs = $row.find( 'ul.cmb-attach-list li' ).length > 0;\n\n\t\t\t\t\t\tif ( ! has_LIs ) {\n\t\t\t\t\t\t\tadd_required( $row );\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tremove_required( $row );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif ( ! val ) {\n\t\t\t\t\t\t\tadd_required( $row );\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tremove_required( $row );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\t\tif ( $first_error_row ) {\n\t\t\t\tevt.preventDefault();\n\t\t\t\talert( '<?php _e( 'The following fields are required and highlighted below:', 'cmb2' ); ?> ' + labels.join( ', ' ) );\n\t\t\t\t$htmlbody.animate({\n\t\t\t\t\tscrollTop: ( $first_error_row.offset().top - 200 )\n\t\t\t\t}, 1000);\n\t\t\t} else {\n\t\t\t\t// Feel free to comment this out or remove\n\t\t\t\talert( 'submission is good!' );\n\t\t\t}\n\n\t\t}\n\n\t\t$form.on( 'submit', checkValidation );\n\t});\n\t</script>\n\t<?php\n}\n\nadd_action( 'cmb2_after_form', 'cmb2_after_form_do_js_validation', 10, 2 );\n"
  },
  {
    "path": "javascript/dynamically-change-group-field-title-from-subfield.php",
    "content": "<?php\n/**\n * Custom Repeatable Group.\n * The \"group_title\" will display the value of the 'title' sub-field, if it exists,\n * or fall back to the default CMB2 group title method.\n */\n\n/**\n * Hook in and add a metabox to demonstrate repeatable grouped fields\n */\nfunction yourprefix_register_repeatable_group_field_title_example() {\n\n\t/**\n\t * Repeatable Field Groups\n\t */\n\t$cmb_group = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_group_titles_metabox',\n\t\t'title'        => __( 'Repeating Field Group with Updating Titles', 'cmb2' ),\n\t\t'object_types' => array( 'page', ),\n\t\t'show_in_rest' => 'read_and_write',\n\t) );\n\n\t$group_field_id = $cmb_group->add_field( array(\n\t\t'id'          => 'yourprefix_group_titles_demo',\n\t\t'type'        => 'group',\n\t\t'description' => __( 'Generates reusable form entries', 'cmb2' ),\n\t\t'options'     => array(\n\t\t\t'group_title'    => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number\n\t\t\t'add_button'     => __( 'Add Another Entry', 'cmb2' ),\n\t\t\t'remove_button'  => __( 'Remove Entry', 'cmb2' ),\n\t\t\t'sortable'       => true, // beta\n\t\t),\n\t\t'after_group' => 'yourprefix_add_js_for_repeatable_titles',\n\t) );\n\n\t$cmb_group->add_group_field( $group_field_id, array(\n\t\t'name' => 'Title',\n\t\t'id'   => 'title',\n\t\t'type' => 'text',\n\t) );\n\n\t$cmb_group->add_group_field( $group_field_id, array(\n\t\t'name' => 'Description',\n\t\t'id'   => 'description',\n\t\t'type' => 'textarea_small',\n\t) );\n}\nadd_action( 'cmb2_init', 'yourprefix_register_repeatable_group_field_title_example' );\n\nfunction yourprefix_add_js_for_repeatable_titles() {\n\tadd_action( is_admin() ? 'admin_footer' : 'wp_footer', 'yourprefix_add_js_for_repeatable_titles_to_footer' );\n}\n\nfunction yourprefix_add_js_for_repeatable_titles_to_footer() {\n\t?>\n\t<script type=\"text/javascript\">\n\tjQuery( function( $ ) {\n\t\tvar $box = $( document.getElementById( 'yourprefix_group_titles_metabox' ) );\n\n\t\tvar replaceTitles = function() {\n\t\t\t$box.find( '.cmb-group-title' ).each( function() {\n\t\t\t\tvar $this = $( this );\n\t\t\t\tvar txt = $this.next().find( '[id$=\"title\"]' ).val();\n\t\t\t\tvar rowindex;\n\n\t\t\t\tif ( ! txt ) {\n\t\t\t\t\ttxt = $box.find( '[data-grouptitle]' ).data( 'grouptitle' );\n\t\t\t\t\tif ( txt ) {\n\t\t\t\t\t\trowindex = $this.parents( '[data-iterator]' ).data( 'iterator' );\n\t\t\t\t\t\ttxt = txt.replace( '{#}', ( rowindex + 1 ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( txt ) {\n\t\t\t\t\t$this.text( txt );\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\t\tvar replaceOnKeyUp = function( evt ) {\n\t\t\tvar $this = $( evt.target );\n\t\t\tvar id = 'title';\n\n\t\t\tif ( evt.target.id.indexOf(id, evt.target.id.length - id.length) !== -1 ) {\n\t\t\t\t$this.parents( '.cmb-row.cmb-repeatable-grouping' ).find( '.cmb-group-title' ).text( $this.val() );\n\t\t\t}\n\t\t};\n\n\t\t$box\n\t\t\t.on( 'cmb2_add_row cmb2_remove_row cmb2_shift_rows_complete', replaceTitles )\n\t\t\t.on( 'keyup', replaceOnKeyUp );\n\n\t\treplaceTitles();\n\t});\n\t</script>\n\t<?php\n}\n"
  },
  {
    "path": "javascript/limit-number-of-multiple-repeat-groups.php",
    "content": "<?php\n/**\n * Set a max limit on the number of times\n * a repeating group can be added for various groups in one page\n */\n/**\n * Setup the groups fields metabox\n */\nfunction js_limited_group_setup() {\n\t/**\n\t * Repeatable Field Groups\n\t */\n\t$cmb_one = new_cmb2_box( array(\n\t\t'id'           => 'field_group_test_one',\n\t\t'title'        => __( 'Repeating Field Group One', 'cmb2' ),\n\t\t'object_types' => array( 'page', ),\n\t\t'rows_limit'   => 3, // custom attribute to use in our JS\n    'groupId' => '_cmb2_repeat_group_one', // custom attribute to use in our JS to retrieve the ID of the group that should be handled\n\t) );\n  \n  $cmb_two = new_cmb2_box( array(\n\t\t'id'           => 'field_group_test_two',\n\t\t'title'        => __( 'Repeating Field Group Two', 'cmb2' ),\n\t\t'object_types' => array( 'page', ),\n\t\t'rows_limit'   => 2, // custom attribute to use in our JS\n    'groupId' => '_cmb2_repeat_group_two', // custom attribute to use in our JS to retrieve the ID of the group that should be handled\n\t) );\n\t\n  $group_one = $cmb_one->add_field( array(\n\t\t'id'          => '_cmb2_repeat_group_one',\n\t\t'type'        => 'group',\n\t\t'description' => __( 'Generates reusable form entries', 'cmb2' ),\n\t\t'options'     => array(\n\t\t\t'group_title'   => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number\n\t\t\t'add_button'    => __( 'Add Another Entry', 'cmb2' ),\n\t\t\t'remove_button' => __( 'Remove Entry', 'cmb2' ),\n\t\t\t'sortable'      => true, // beta\n\t\t),\n\t) );\n\t\n  $cmb_one->add_group_field( $group_one, array(\n\t\t'name'       => 'Entry Title',\n\t\t'id'         => 'title',\n\t\t'type'       => 'text',\n\t) );\n\t\n  $cmb_one->add_group_field( $group_one, array(\n\t\t'name' => 'Description',\n\t\t'desc' => 'Write a short description for this entry',\n\t\t'id'   => 'description',\n\t\t'type' => 'textarea_small',\n\t) );\n  \n  $group_two = $cmb_two->add_field( array(\n\t\t'id'          => '_cmb2_repeat_group_two',\n\t\t'type'        => 'group',\n\t\t'description' => __( 'Generates reusable form entries', 'cmb2' ),\n\t\t'options'     => array(\n\t\t\t'group_title'   => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number\n\t\t\t'add_button'    => __( 'Add Another Entry', 'cmb2' ),\n\t\t\t'remove_button' => __( 'Remove Entry', 'cmb2' ),\n\t\t\t'sortable'      => true, // beta\n\t\t),\n\t) );\n\t\n  $cmb_two->add_group_field( $group_two, array(\n\t\t'name'       => 'Entry Title',\n\t\t'id'         => 'title',\n\t\t'type'       => 'text',\n\t) );\n\t\n  $cmb_two->add_group_field( $group_two, array(\n\t\t'name' => 'Description',\n\t\t'desc' => 'Write a short description for this entry',\n\t\t'id'   => 'description',\n\t\t'type' => 'textarea_small',\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'js_limited_group_setup', 9999 );\n\n\n$repeater_metaboxes = array('field_group_test_one','field_group_test_two'); // IDs of the metabox containing the repeater group\n\nforeach ($repeater_metaboxe as $value) {\n    add_action( 'cmb2_after_post_form_'.$value, 'js_limit_group_repeat', 10, 2);\n}\n\nfunction js_limit_group_repeat( $post_id, $cmb ) {\n\t// Grab the custom attribute to determine the limit\n\t$limit = absint( $cmb->prop( 'rows_limit' ) );\n\t$limit = $limit ? $limit : 0;\n  $group =  $cmb->prop( 'groupId' )\n\t?>\n\t<script type=\"text/javascript\">\n\tjQuery(document).ready(function($){\n\t\t// Only allow 3 groups\n\t\tvar limit            = <?php echo $limit; ?>;\n\t\tvar fieldGroupId     = '<?php echo $group; ?>'; \n\t\tvar $fieldGroupTable = $( document.getElementById( fieldGroupId + '_repeat' ) );\n\t\tvar countRows = function() {\n\t\t\treturn $fieldGroupTable.find( '> .cmb-row.cmb-repeatable-grouping' ).length;\n\t\t};\n\t\tvar disableAdder = function() {\n\t\t\t$fieldGroupTable.find('.cmb-add-group-row.button-secondary').prop( {disabled: true} );\n\t\t};\n\t\tvar enableAdder = function() {\n\t\t\t$fieldGroupTable.find('.cmb-add-group-row.button-secondary').prop( {disabled: false} );\n\t\t};\n\t\t$fieldGroupTable\n\t\t\t.on( 'cmb2_add_row', function() {\n\t\t\t\tif ( countRows() >= limit ) {\n\t\t\t\t\tdisableAdder();\n\t\t\t\t}\n\t\t\t})\n\t\t\t.on( 'cmb2_remove_row', function() {\n\t\t\t\tif ( countRows() < limit ) {\n\t\t\t\t\tenableAdder();\n\t\t\t\t}\n\t\t\t});\n\t});\n\t</script>\n\t<?php\n}\n"
  },
  {
    "path": "javascript/limit-number-of-repeat-fields.php",
    "content": "<?php\n/**\n * Set a max limit on the number of times\n * a repeating field can be added.\n */\n\n/**\n * Setup the metabox\n */\nfunction js_limited_repeat_field_setup() {\n\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'test_limit_rows',\n\t\t'title'        => __( 'Repeating Field Group', 'cmb2' ),\n\t\t'object_types' => array( 'page', ),\n\t\t'rows_limit'   => 3,\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name' => 'Entry Title',\n\t\t'id'   => 'text_repeat_test',\n\t\t'type' => 'text',\n\t\t'repeatable' => true, // Repeatable fields are supported w/in repeatable groups (for most types)\n\t) );\n}\nadd_action( 'cmb2_admin_init', 'js_limited_repeat_field_setup', 9999 );\n\nfunction js_limit_field_repeat( $post_id, $cmb ) {\n\t// Grab the custom attribute to determine the limit\n\t$limit = absint( $cmb->prop( 'rows_limit' ) );\n\t$limit = $limit ? $limit : 0;\n\t?>\n\t<script type=\"text/javascript\">\n\tjQuery(document).ready(function($){\n\t\t// Only allow 3 groups\n\t\tvar limit        = <?php echo $limit; ?>;\n\t\tvar fieldGroupId = 'text_repeat_test';\n\t\tvar $fieldTable  = $( document.getElementById( fieldGroupId + '_repeat' ) );\n\t\tvar $repeatWrap  = $fieldTable.find( '.cmb-repeat-table.cmb-nested' ).first();\n\n\t\tvar countRows = function() {\n\t\t\treturn $repeatWrap.find( '.cmb-row.cmb-repeat-row' ).length;\n\t\t};\n\n\t\tvar disableAdder = function() {\n\t\t\t$repeatWrap.parents( '.cmb-repeat.table-layout' ).find('.cmb-add-row-button').prop( 'disabled', true );\n\t\t};\n\n\t\tvar enableAdder = function() {\n\t\t\t$repeatWrap.parents( '.cmb-repeat.table-layout' ).find('.cmb-add-row-button').prop( 'disabled', false );\n\t\t};\n\n\t\t$fieldTable\n\t\t\t.on( 'cmb2_add_row', function( evt, row ) {\n\t\t\t\tvar $row = $( row );\n\t\t\t\t$repeatWrap = $row.parents( '.cmb-repeat-table.cmb-nested' );\n\t\t\t\tif ( countRows() >= limit ) {\n\t\t\t\t\tdisableAdder();\n\t\t\t\t}\n\t\t\t})\n\t\t\t.on( 'cmb2_remove_row', function() {\n\t\t\t\tif ( countRows() < limit ) {\n\t\t\t\t\tenableAdder();\n\t\t\t\t}\n\t\t\t});\n\t});\n\t</script>\n\t<?php\n}\nadd_action( 'cmb2_after_post_form_test_limit_rows', 'js_limit_field_repeat', 10, 2 );\n"
  },
  {
    "path": "javascript/limit-number-of-repeat-groups.php",
    "content": "<?php\n/**\n * Set a max limit on the number of times\n * a repeating group can be added.\n */\n\n/**\n * Setup the group field metabox\n */\nfunction js_limited_group_setup() {\n\t/**\n\t * Repeatable Field Groups\n\t */\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'field_group_test',\n\t\t'title'        => __( 'Repeating Field Group', 'cmb2' ),\n\t\t'object_types' => array( 'page', ),\n\t\t'rows_limit'   => 3, // custom attribute to use in our JS\n\t) );\n\n\t$group_id = $cmb->add_field( array(\n\t\t'id'          => '_cmb2_repeat_group',\n\t\t'type'        => 'group',\n\t\t'description' => __( 'Generates reusable form entries', 'cmb2' ),\n\t\t'options'     => array(\n\t\t\t'group_title'   => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number\n\t\t\t'add_button'    => __( 'Add Another Entry', 'cmb2' ),\n\t\t\t'remove_button' => __( 'Remove Entry', 'cmb2' ),\n\t\t\t'sortable'      => true, // beta\n\t\t),\n\t) );\n\n\t$cmb->add_group_field( $group_id, array(\n\t\t'name'       => 'Entry Title',\n\t\t'id'         => 'title',\n\t\t'type'       => 'text',\n\t) );\n\n\t$cmb->add_group_field( $group_id, array(\n\t\t'name' => 'Description',\n\t\t'desc' => 'Write a short description for this entry',\n\t\t'id'   => 'description',\n\t\t'type' => 'textarea_small',\n\t) );\n}\nadd_action( 'cmb2_admin_init', 'js_limited_group_setup', 9999 );\n\nfunction js_limit_group_repeat( $post_id, $cmb ) {\n\t// Grab the custom attribute to determine the limit\n\t$limit = absint( $cmb->prop( 'rows_limit' ) );\n\t$limit = $limit ? $limit : 0;\n\t?>\n\t<script type=\"text/javascript\">\n\tjQuery(document).ready(function($){\n\t\t// Only allow 3 groups\n\t\tvar limit            = <?php echo $limit; ?>;\n\t\tvar fieldGroupId     = '_cmb2_repeat_group'; // This should match the ID of your group field.\n\t\tvar $fieldGroupTable = $( document.getElementById( fieldGroupId + '_repeat' ) );\n\n\t\tvar countRows = function() {\n\t\t\treturn $fieldGroupTable.find( '> .cmb-row.cmb-repeatable-grouping' ).length;\n\t\t};\n\n\t\tvar disableAdder = function() {\n\t\t\t$fieldGroupTable.find('.cmb-add-group-row').prop( 'disabled', true );\n\t\t};\n\t\t\n\t\tif ( countRows() >= limit ) {\n\t\t\tdisableAdder();\n\t\t}\t\t\t\n\n\t\tvar enableAdder = function() {\n\t\t\t$fieldGroupTable.find('.cmb-add-group-row').prop( 'disabled', false );\n\t\t};\n\t\t\n\t\tif ( countRows() < limit ) {\n                \tenableAdder();\n            \t}\n\n\t\t$fieldGroupTable\n\t\t\t.on( 'cmb2_add_row', function() {\n\t\t\t\tif ( countRows() >= limit ) {\n\t\t\t\t\tdisableAdder();\n\t\t\t\t}\n\t\t\t})\n\t\t\t.on( 'cmb2_remove_row', function() {\n\t\t\t\tif ( countRows() < limit ) {\n\t\t\t\t\tenableAdder();\n\t\t\t\t}\n\t\t\t});\n\t});\n\t</script>\n\t<?php\n}\n// Replace `field_group_test` with the ID of the CMB2 meta box in question e.g. this\n// should match the value of $cmb->id in the above example.\nadd_action( 'cmb2_after_post_form_field_group_test', 'js_limit_group_repeat', 10, 2 );\n"
  },
  {
    "path": "metaboxes/README.md",
    "content": "Metaboxes\n==========\n\nThese are examples of different metabox configurations."
  },
  {
    "path": "misc/README.md",
    "content": "Miscellaneous Snippets\n==========\n\nRandom CMB2 and WordPress snippets\n"
  },
  {
    "path": "misc/add-wrap-to-group-of-fields.php",
    "content": "<?php\n/**\n * Example of adding an opening/closing wrap around a group of fields.\n * Gif: http://b.ustin.co/12Uba\n */\n\nadd_action( 'cmb2_admin_init', 'yourprefix_register_demo_metabox' );\n/**\n * Hook in and add a demo metabox. Can only happen on the 'cmb2_admin_init' or 'cmb2_init' hook.\n */\nfunction yourprefix_register_demo_metabox() {\n\t$prefix = 'yourprefix_demo_';\n\n\t$cmb_demo = new_cmb2_box( array(\n\t\t'id'            => $prefix . 'metabox',\n\t\t'title'         => esc_html__( 'Test Metabox', 'cmb2' ),\n\t\t'object_types'  => array( 'page', ), // Post type\n\t) );\n\n\t// Markup to add a metabox-like toggle box: http://b.ustin.co/12Uba\n\t$advanced_open = '\n\t<div class=\"advanced-toggle advanced-toggle-wrap postbox closed\">\n\t<button type=\"button\" class=\"handlediv button-link\" aria-expanded=\"true\"><span class=\"screen-reader-text\">Click to toggle</span><span class=\"toggle-indicator\" aria-hidden=\"true\"></span></button>\n\t<h3 class=\"hndle ui-sortable-handle\"><span>Toggle Advanced Options</span></h3>\n\t<div class=\"inside\">\n\t';\n\t$advanced_close = '</div></div>';\n\n\t$cmb->add_field( array(\n\t\t'before_row' => $advanced_open,\n\t\t'name' => esc_html__( 'Test Text', 'cmb2' ),\n\t\t'desc' => esc_html__( 'field description (optional)', 'cmb2' ),\n\t\t'id'   => $prefix . 'text',\n\t\t'type' => 'text',\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name' => esc_html__( 'Test Text Small', 'cmb2' ),\n\t\t'desc' => esc_html__( 'field description (optional)', 'cmb2' ),\n\t\t'id'   => $prefix . 'textsmall',\n\t\t'type' => 'text_small',\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name' => esc_html__( 'Test Text Medium', 'cmb2' ),\n\t\t'desc' => esc_html__( 'field description (optional)', 'cmb2' ),\n\t\t'id'   => $prefix . 'textmedium',\n\t\t'type' => 'text_medium',\n\t\t'after_row' => $advanced_close,\n\t) );\n\n\t// Optionally do other fields here...\n\n}\n"
  },
  {
    "path": "misc/adding-wordcount-to-cmb2-wysiwyg-field.php",
    "content": "<?php\n/**\n * Register a wysiwyg field and add the wordcount below it.\n */\n\nfunction yourprefix_feat_img_fields() {\n\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'hold-my-wysiwyg',\n\t\t'object_types' => array( 'post' ),\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name'  => __( 'WYSIWYG content', 'YOURTEXTDOMAIN' ),\n\t\t'id'    => 'wysiwyg_content',\n\t\t'type'  => 'wysiwyg',\n\t\t'after' => 'cmb2_wysiwyg_word_counter',\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'yourprefix_feat_img_fields' );\n\n/**\n * Outputs wordcount for wysiwyg field.\n *\n * Basically copied from: https://github.com/WordPress/WordPress/blob/3099f4d9edc5f2f2b6ef8becc966135edde909a8/wp-admin/js/post.js#L1219-L1271\n * and: https://github.com/WordPress/WordPress/blob/3099f4d9edc5f2f2b6ef8becc966135edde909a8/wp-admin/edit-form-advanced.php#L714\n */\nfunction cmb2_wysiwyg_word_counter( $args, $field ) {\n\twp_enqueue_script( 'word-count', array( 'jquery', 'underscore', 'word-count' ) );\n\t?>\n\n\t<p id=\"<?php echo $field->id(); ?>-word-count\" class=\"hide-if-no-js cmb2-wysiwyg-word-count\"><?php printf( __( 'Word count: %s' ), '<span class=\"word-count\">0</span>' ); ?></p>\n\n\t<script type=\"text/javascript\">\n\t\tjQuery( function( $ ) {\n\t\t\tvar editorId = '<?php echo $field->id(); ?>';\n\t\t\t/**\n\t\t\t * TinyMCE word count display\n\t\t\t */\n\t\t\t( function( $, counter ) {\n\t\t\t\t$( function() {\n\t\t\t\t\tvar $content = $( '#' + editorId ),\n\t\t\t\t\t\t$count = $( '#' + editorId + '-word-count' ).find( '.word-count' ),\n\t\t\t\t\t\tprevCount = 0,\n\t\t\t\t\t\tcontentEditor;\n\n\t\t\t\t\t/**\n\t\t\t\t\t * Get the word count from TinyMCE and display it\n\t\t\t\t\t */\n\t\t\t\t\tfunction update() {\n\t\t\t\t\t\tvar text, count;\n\n\t\t\t\t\t\tif ( ! contentEditor || contentEditor.isHidden() ) {\n\t\t\t\t\t\t\ttext = $content.val();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = contentEditor.getContent( { format: 'raw' } );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcount = counter.count( text );\n\n\t\t\t\t\t\tif ( count !== prevCount ) {\n\t\t\t\t\t\t\t$count.text( count );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprevCount = count;\n\t\t\t\t\t}\n\n\t\t\t\t\t/**\n\t\t\t\t\t * Bind the word count update triggers.\n\t\t\t\t\t *\n\t\t\t\t\t * When a node change in the main TinyMCE editor has been triggered.\n\t\t\t\t\t * When a key has been released in the plain text content editor.\n\t\t\t\t\t */\n\t\t\t\t\t$( document ).on( 'tinymce-editor-init', function( event, editor ) {\n\t\t\t\t\t\tif ( editor.id !== editorId ) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontentEditor = editor;\n\n\t\t\t\t\t\teditor.on( 'nodechange keyup', _.debounce( update, 1000 ) );\n\t\t\t\t\t} );\n\n\t\t\t\t\t$content.on( 'input keyup', _.debounce( update, 1000 ) );\n\n\t\t\t\t\tupdate();\n\t\t\t\t} );\n\t\t\t} )( jQuery, new wp.utils.WordCounter() );\n\n\t\t});\n\n\t</script>\n\t<?php\n}\n"
  },
  {
    "path": "misc/cmb2-field-in-publish-box.php",
    "content": "<?php\n/**\n * Add a CMB2 field to the WordPress publish box.\n *\n * @author Chris Reynolds <chris@hmn.md>\n * @link   https://joebuckle.me/quickie/wordpress-add-options-to-post-admin-publish-meta-box/\n * @link   https://github.com/CMB2/CMB2-Snippet-Library/blob/master/misc/outputting-forms-outside-metaboxes.php\n * @link   https://codex.wordpress.org/Plugin_API/Action_Reference/post_submitbox_misc_actions\n */\n\n/**\n * Register the CMB2 metabox.\n */\nfunction yourprefix_cmb2_fields() {\n\t$prefix = '_yourprefix_';\n\n\t$cmb = new_cmb2_box( [\n\t\t'id'           => $prefix . 'publish_box',\n\t\t'object_types' => [ 'post' ],               // Any public post type.\n\t\t'show_names'   => false,                    // Disables the display for the CMB2 label.\n\t] );\n\n\t$cmb->add_field( [\n\t\t'before' => '<label for=\"' . $prefix . 'field\">' . __( 'Your field name', 'cmb2' ) . '</label>', // Output the label w/o the default CMB2 styles.\n\t\t'id'   => $prefix . 'field',\n\t\t'desc' => __( 'Field description (optional)', 'cmb2' ),\n\t\t'type' => 'text', // Any valid CMB2 field type.\n\t\t'attributes' => [\n\t\t\t'style' => 'max-width: 80%;', // Cleans up the default CMB2 styles a bit. Move to stylesheet?\n\t\t]\n\t] );\n}\n\nadd_action( 'post_submitbox_misc_actions', 'yourprefix_filter_publish_box' );\n\n/**\n * Display the CMB2 form in the WordPress publish metabox.\n *\n * @param object $post The WP_Post object.\n */\nfunction yourprefix_filter_publish_box( $post ) {\n\t$cmb = cmb2_get_metabox( '_yourprefix_publish_box' ); // Must match the box ID.\n\n\tif ( in_array( $post->post_type, $cmb->prop( 'object_types' ), true ) ) {\n\t\t$cmb->show_form();\n\t}\n}\n\nadd_action( 'cmb2_admin_init', 'yourprefix_cmb2_fields' );\n"
  },
  {
    "path": "misc/helper-functions.php",
    "content": "<?php\n/**\n * Miscellaneous CMB2 Helper functions\n */\n\n\n/**\n * Save fields data from an array of data (Likely $_POST data.)\n *\n * @link  https://wordpress.org/support/topic/sanitizing-data-outside-metabox-context Forum post\n *\n * @param  mixed  $meta_box_id  Metabox ID (or metabox config array)\n * @param  int    $object_id    ID of post/user/comment/options-page to save the data against\n * @param  array  $data_to_save Array of key => value data for saving. Likely $_POST data.\n */\nfunction cmb2_save_metabox_fields_data( $meta_box_id, $object_id, array $data_to_save ) {\n\t$cmb = cmb2_get_metabox( $meta_box_id, $object_id );\n\t$cmb->save_fields( $object_id, $cmb->object_type(), $data_to_save );\n}\n\n/**\n * Returns array of sanitized field values (without saving them) from\n * provided array of values (Likely $_POST data.)\n *\n * Combined with the metabox `'save_fields' => false` config option, you can use this to\n * save the box data somewhere else.\n *\n * @link  https://wordpress.org/support/topic/sanitizing-data-outside-metabox-context Forum post\n *\n * @param  mixed  $meta_box_id  Metabox ID (or metabox config array)\n * @param  int    $object_id    ID of post/user/comment/options-page to save the data against\n * @param  array  $data_to_save Array of key => value data for saving. Likely $_POST data.\n */\nfunction cmb2_get_metabox_sanitized_values( $meta_box_id, array $data_to_save ) {\n\t$cmb = cmb2_get_metabox( $meta_box_id );\n\t$cmb->get_sanitized_values( $data_to_save );\n}\n"
  },
  {
    "path": "misc/outputting-cmb2-fields-in-featured-image-metabox.php",
    "content": "<?php\n/**\n * Display CMB2 fields in the featured-image metabox.\n */\n\nfunction yourprefix_feat_img_fields() {\n\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'feat-image-fields',\n\t\t'object_types' => array( 'post' ),\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name' => 'Featured Image Position',\n\t\t'id'   => 'feat_img_placement',\n\t\t'type' => 'select',\n\t\t'options' => array(\n\t\t\t''      => 'Center', // The default -- no value. Keeps out of the database.\n\t\t\t'left'  => 'Left',\n\t\t\t'right' => 'Right',\n\t\t),\n\t\t'before' => '<style>\n\t\t#cmb2-metabox-feat-image-fields .cmb-th,\n\t\t#cmb2-metabox-feat-image-fields .cmb-td,\n\t\t#side-sortables .cmb2-wrap #cmb2-metabox-feat-image-fields .cmb-row {\n\t\t\tpadding: 0;\n\t\t}\n\t\t</style>',\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'yourprefix_feat_img_fields' );\n\nfunction yourprefix_feat_img_output_fields( $content, $post_id, $thumbnail_id ) {\n\t$cmb = cmb2_get_metabox( 'feat-image-fields' );\n\n\tif ( $cmb && in_array( get_post_type(), $cmb->prop( 'object_types' ), 1 ) ) {\n\t\tob_start();\n\t\t$cmb->show_form();\n\t\t// grab the data from the output buffer and add it to our $content variable\n\t\t$content .= ob_get_clean();\n\t}\n\n\treturn $content;\n}\nadd_filter( 'admin_post_thumbnail_html', 'yourprefix_feat_img_output_fields', 10, 3 );\n"
  },
  {
    "path": "misc/outputting-forms-outside-metaboxes.php",
    "content": "<?php\n/**\n * Display CMB2 fields in other areas of the post screen (not in metaboxes)\n */\n\n/**\n * Hook in and add a demo metabox.\n */\nfunction yourprefix_register_cmb2_fields() {\n\n\t$cmb = new_cmb2_box( array(\n\t\t'id'            => '_yourprefix_display_title',\n\t\t'object_types'  => array( 'page' ),\n\t\t//'title' => '', omit the 'title' field to keep the normal wp metabox from displaying\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name' => 'Display title for this page?',\n\t\t'id'   => '_yourprefix_display_title',\n\t\t'type' => 'checkbox',\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name' => 'A textarea',\n\t\t'id'   => '_yourprefix_display_title_text',\n\t\t'type' => 'textarea',\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'yourprefix_register_cmb2_fields' );\n\n\n/**\n * Display checkbox metabox below title field\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L517-L523\n */\nfunction yourprefix_output_custom_mb_location() {\n\t$cmb = cmb2_get_metabox( '_yourprefix_display_title' );\n\n\tif ( in_array( get_post_type(), $cmb->prop( 'object_types' ), 1 ) ) {\n\t\t$cmb->show_form();\n\t}\n}\nadd_action( 'edit_form_after_title', 'yourprefix_output_custom_mb_location' );\n\n/**\n * More hooks in the post-editor screen as of 4.1\n */\n\n/**\n* Display checkbox metabox below wysiwyg editor field\n* @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L560-L567\n*/\n// add_action( 'edit_form_after_editor', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L217-L225\n */\n// add_action( 'dbx_post_advanced', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L241-L249\n */\n// add_action( 'add_meta_boxes', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L416-L423\n */\n// add_action( 'post_edit_form_tag', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L447-L456\n */\n// add_action( 'edit_form_top', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L480-L487\n */\n// add_action( 'edit_form_before_permalink', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L516-L523\n */\n// add_action( 'edit_form_after_title', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L560-L567\n */\n// add_action( 'edit_form_after_editor', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L574-L597\n */\n// add_action( 'submitpost_box', 'yourprefix_output_custom_mb_location' );\n// add_action( 'submitpage_box', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L609-L628\n */\n// add_action( 'edit_form_advanced', 'yourprefix_output_custom_mb_location' );\n// add_action( 'edit_page_form', 'yourprefix_output_custom_mb_location' );\n\n/**\n * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L636-L643\n */\n// add_action( 'dbx_post_sidebar', 'yourprefix_output_custom_mb_location' );\n"
  },
  {
    "path": "misc/replace-wp-excerpt-with-cmb2-field.php",
    "content": "<?php\n/**\n * If you want to replace the default WordPress excerpt field with CMB2 field,\n * it can be done with the following snippets.\n */\n\nfunction cmb2_register_excerpt_replacement_box() {\n\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'cmb2_excerpt',\n\t\t'title'        => 'Excerpt',\n\t\t'object_types' => array( 'post', ), // Post type\n\t\t// 'context'      => 'side',\n\t) );\n\n\t$cmb->add_field( array(\n\t\t/*\n\t\t * As long as the 'id' matches the name field of the regular WP field,\n\t\t * WP will handle the saving for you.\n\t\t */\n\t\t'id'        => 'excerpt',\n\t\t'name'      => 'Excerpt',\n\t\t'desc'      => 'Excerpts are optional hand-crafted summaries of your content that can be used in your theme. <a href=\"https://codex.wordpress.org/Excerpt\" target=\"_blank\">Learn more about manual excerpts.</a>',\n\t\t'type'      => 'textarea',\n\t\t'escape_cb' => false,\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'cmb2_register_excerpt_replacement_box' );\n\n\n/**\n * Remove the default WordPress excerpt field.\n */\nfunction cmb2_admin_hide_excerpt_field() {\n\tadd_action( 'add_meta_boxes', '_cmb2_admin_hide_excerpt_field' );\n}\nadd_filter( 'admin_init', 'cmb2_admin_hide_excerpt_field' );\n\nfunction _cmb2_admin_hide_excerpt_field() {\n\t$screen = get_current_screen();\n\n\tif ( isset( $screen->post_type ) && 'post' === $screen->post_type ) {\n\t\tremove_meta_box( 'postexcerpt', null, 'normal' );\n\t}\n}\n\n\n/**\n * Override the WordPress Excerpt field\n */\nfunction cmb2_override_excerpt_display( $data, $post_id ) {\n\treturn get_post_field( 'post_excerpt', $post_id );\n}\nadd_filter( 'cmb2_override_excerpt_meta_value', 'cmb2_override_excerpt_display', 10, 2 );\n\n/*\n * WP will handle the saving for us, so don't save to meta.\n */\nadd_filter( 'cmb2_override_excerpt_meta_save', '__return_true' );\n"
  },
  {
    "path": "misc/replace-wp-title-content-thumbnail-with-cmb2-fields.php",
    "content": "<?php\n/**\n * Use CMB2 fields to replace WordPress title/content/thumbnail fields.\n */\n\n/**\n * Register a dummy cpt for demonstration\n */\nfunction register_test_cpt() {\n\t$labels = array(\n\t\t'name'                => 'CPT',\n\t\t'singular_name'       => 'CPT',\n\t\t'menu_name'           => 'CPT',\n\t\t'parent_item_colon'   => 'Parent CPT',\n\t\t'all_items'           => 'All CPT',\n\t\t'view_item'           => 'View CPT',\n\t\t'add_new_item'        => 'Add New CPT',\n\t\t'add_new'             => 'Add New',\n\t\t'edit_item'           => 'Edit CPT',\n\t\t'update_item'         => 'Update CPT',\n\t\t'search_items'        => 'Search CPT',\n\t\t'not_found'           => 'Not Found',\n\t\t'not_found_in_trash'  => 'Not found in Trash',\n\t);\n\n  register_post_type( 'cpt', array(\n\t\t'labels' => $labels,\n\t\t'supports' => array( 'title' ),\n\t\t'supports' => array( '' ),\n\t\t'public' => true,\n\t\t'has_archive' => true,\n\t) );\n}\nadd_action( 'init', 'register_test_cpt' );\n\nfunction register_test_cpt_metabox() {\n\n\t$cmb_subsub = new_cmb2_box( array (\n\t\t'id'           => 'metabox',\n\t\t'title'        => 'Edit',\n\t\t'object_types' => array( 'cpt' ),\n\t) );\n\n\t$cmb_subsub->add_field( array(\n\t\t'id'      => 'post_title', // Saves to WP post title, allows the_title()\n\t\t'name'    => 'Title',\n\t\t'desc'    => 'Provide a title.',\n\t\t'default' => '',\n\t\t'type'    => 'text',\n\t) );\n\n\t$cmb_subsub->add_field( array(\n\t\t'id'   => 'post_content', // Saves to WP post content, allows the_content()\n\t\t'name' => 'Description',\n\t\t'desc' => 'Enter a brief description',\n\t\t'type' => 'textarea', // wysiwyg is problematic when replacing post_content\n\t) );\n\n\t$cmb_subsub->add_field( array(\n\t\t'id'      => '_thumbnail', // Saves to WP post thumbnail, allows the_post_thumbnail()\n\t\t'name'    => 'Image',\n\t\t'desc'    => 'Upload/Select an image.',\n\t\t'type'    => 'file',\n\t\t'options' => array(\n\t\t\t'url' => false,\n\t\t),\n\t\t'text' => array(\n\t\t\t'add_upload_file_text' => 'Add Image'\n\t\t),\n\t) );\n\n};\nadd_action( 'cmb2_admin_init', 'register_test_cpt_metabox' );\n\n/*\n * Override the title/content field retrieval so CMB2 doesn't look in post-meta.\n */\nfunction cmb2_override_post_title_display( $data, $post_id ) {\n\treturn get_post_field( 'post_title', $post_id );\n}\nfunction cmb2_override_post_content_display( $data, $post_id ) {\n\treturn get_post_field( 'post_content', $post_id );\n}\nadd_filter( 'cmb2_override_post_title_meta_value', 'cmb2_override_post_title_display', 10, 2 );\nadd_filter( 'cmb2_override_post_content_meta_value', 'cmb2_override_post_content_display', 10, 2 );\n\n/*\n * WP will handle the saving for us, so don't save title/content to meta.\n */\nadd_filter( 'cmb2_override_post_title_meta_save', '__return_true' );\nadd_filter( 'cmb2_override_post_content_meta_save', '__return_true' );\n"
  },
  {
    "path": "modified-field-types/README.md",
    "content": "Modified Field Types\n==========\n\nThese are examples of using the built-in [CMB2 field-types](https://github.com/WebDevStudios/CMB2/wiki/Field-Types), but modifiying them with the [available parameters](https://github.com/WebDevStudios/CMB2/wiki/Field-Types#common-field-parameters)."
  },
  {
    "path": "modified-field-types/modify-button-text-for-file-type.php",
    "content": "<?php\n\n// Do your $cmb = new_cmb2_box(), then add this field.\n// See https://github.com/WebDevStudios/CMB2/blob/develop/example-functions.php for more.\n\n/**\n * The 'file' type accepts any type of file which WordPress allows.\n * We want to change our text to indicate that they should be images.\n */\n$cmb->add_field( array(\n\t'name' => 'Image',\n\t'desc' => 'Upload an image.',\n\t'id'   => '_jt_cmb2_image',\n\t'type' => 'file',\n\t'options' => array(\n\t\t'add_upload_file_text' => __( 'Add or Upload Image', 'jt_cmb2' ),\n\t),\n) );\n"
  },
  {
    "path": "modified-field-types/readonly-field-type.php",
    "content": "<?php\n\n// Do your $cmb = new_cmb2_box(), then add this field.\n// See https://github.com/WebDevStudios/CMB2/blob/develop/example-functions.php for more.\n\n/**\n * A CMB2 Readonly Field Type\n */\n$cmb->add_field( array(\n\t'name'        => 'Read Only',\n\t'description' => 'The value of this input should be saved somewhere else.',\n\t'id'          => '_jtcmb2_readonly',\n\t'type'        => 'text',\n\t'save_field'  => false, // Otherwise CMB2 will end up removing the value.\n\t'attributes'  => array(\n\t\t'readonly' => 'readonly',\n\t\t'disabled' => 'disabled',\n\t),\n) );\n"
  },
  {
    "path": "options-and-settings-pages/README.md",
    "content": "Options and Settings Pages\n==========\n\nThese snippets demonstrate how to create options page metaboxes or hook into genesis settings metaboxes.\n\nThere are also examples for how you can retrieve an option, using `myprefix_get_option( 'test_text' )`. \n\nObviously replace all instances of `myprefix` with a unique project-specific prefix.\n\n[Check these snippets out](https://github.com/WebDevStudios/CMB2-Snippet-Library/tree/master/helper-functions) if you're looking to modify the form output of the `cmb2_metabox_form` function.\n"
  },
  {
    "path": "options-and-settings-pages/add-cmb2-settings-to-other-settings-pages.php",
    "content": "<?php\n/**\n * CMB2 Network Settings\n * @version 0.1.0\n */\nclass Prefix_Add_CMB2_To_Settings_Page {\n\n\t/**\n \t * Option key, and option page slug\n \t * @var string\n \t */\n\tprotected $key = 'myprefix_settings';\n\n\t/**\n \t * Settings page metabox id\n \t * @var string\n \t */\n\tprotected $metabox_id = 'myprefix_setting_metabox';\n\n\t/**\n \t * Settings page screen id where metabox should show.\n \t * @var string\n \t */\n\tprotected $screen_id = 'options-general';\n\n\t/**\n\t * Holds an instance of the project\n\t *\n\t * @Prefix_Add_CMB2_To_Settings_Page\n\t **/\n\tprotected static $instance = null;\n\n\t/**\n\t * Constructor\n\t * @since 0.1.0\n\t */\n\tprotected function __construct() {}\n\n\t/**\n\t * Get the running object\n\t *\n\t * @return Prefix_Add_CMB2_To_Settings_Page\n\t **/\n\tpublic static function get_instance() {\n\t\tif( is_null( self::$instance ) ) {\n\t\t\tself::$instance = new self();\n\t\t\tself::$instance->hooks();\n\t\t}\n\t\treturn self::$instance;\n\t}\n\n\t/**\n\t * Initiate our hooks\n\t * @since 0.1.0\n\t */\n\tpublic function hooks() {\n\t\tadd_action( 'cmb2_admin_init', array( $this, 'register_metabox' ) );\n\t\tadd_action( 'current_screen', array( $this, 'maybe_save' ) );\n\t\tadd_filter( 'admin_footer' , array( $this , 'maybe_hookup_fields' ), 2 /* Early before all scripts are output. */ );\n\t}\n\n\t/**\n\t * Add the options metabox to the array of metaboxes\n\t * @since  0.1.0\n\t */\n\tfunction register_metabox() {\n\t\t$cmb = new_cmb2_box( array(\n\t\t\t'id'           => $this->metabox_id,\n\t\t\t'hookup'       => false,\n\t\t\t'object_types' => array( 'options-page' ),\n\t\t) );\n\n\t\t// Set our CMB2 fields\n\n\t\t$cmb->add_field( array(\n\t\t\t'name' => __( 'Test Text', 'myprefix' ),\n\t\t\t'desc' => __( 'field description (optional)', 'myprefix' ),\n\t\t\t'id'   => 'test_text',\n\t\t\t'type' => 'text',\n\t\t\t// 'default' => 'Default Text',\n\t\t) );\n\n\t\t$cmb->add_field( array(\n\t\t\t'name'    => __( 'Test Color Picker', 'myprefix' ),\n\t\t\t'desc'    => __( 'field description (optional)', 'myprefix' ),\n\t\t\t'id'      => 'test_colorpicker',\n\t\t\t'type'    => 'colorpicker',\n\t\t\t'default' => '#bada55',\n\t\t) );\n\n\t}\n\n\t/**\n\t * Register our setting to WP\n\t * @since  0.1.0\n\t */\n\tpublic function maybe_save() {\n\t\tif ( empty( $_POST ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$url = wp_get_referer();\n\t\t// Check if our screen id is in the referrer url.\n\t\tif ( false === strpos( $url, $this->screen_id ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Hook into whitelist_options as we know it's only called if the default save-checks have finished.\n\t\tadd_filter( 'whitelist_options', array( $this, 'save_our_options' ) );\n\t}\n\n\t/**\n\t * Simply used as a options.php life-cycle hook to save our settings\n\t * (since there doesn't appear to be any proper hooks)\n\t *\n\t * @since  0.1.0\n\t *\n\t * @param  array  $whitelist_options\n\t *\n\t * @return array\n\t */\n\tpublic function save_our_options( $whitelist_options ) {\n\t\t$cmb = cmb2_get_metabox( $this->metabox_id, $this->key );\n\t\tif ( $cmb ) {\n\n\t\t\t$hookup = new CMB2_hookup( $cmb );\n\n\t\t\tif ( $hookup->can_save( 'options-page' ) ) {\n\t\t\t\t$cmb->save_fields( $this->key, 'options-page', $_POST );\n\t\t\t}\n\t\t}\n\n\t\t// Our saving is done, so cleanup.\n\t\tremove_filter( 'whitelist_options', array( $this, 'save_our_options' ) );\n\n\t\treturn $whitelist_options;\n\t}\n\n\t/**\n\t * Maybe hookup our CMB2 fields.\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function maybe_hookup_fields() {\n\t\t$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : (object) array( 'id' => null );\n\n\t\t// Only show on our screen.\n\t\tif ( $this->screen_id !== $screen->id ) {\n\t\t\treturn;\n\t\t}\n\n\t\tCMB2_hookup::enqueue_cmb_css();\n\t\t$this->admin_page_display();\n\t}\n\n\t/**\n\t * CMB2 fields output\n\t * Wile hide by default in the footer, then use JS to move it inside the form. Hacky, yep.\n\t *\n\t * @since  0.1.0\n\t */\n\tpublic function admin_page_display() {\n\t\t?>\n\t\t<div id=\"cmb2-options-page-<?php echo $this->key; ?>\" class=\"wrap cmb2-options-page <?php echo $this->key; ?>\" style=\"display:none\">\n\t\t\t<?php cmb2_get_metabox( $this->metabox_id, $this->key, 'options-page' )->show_form(); ?>\n\t\t</div>\n\t\t<script type=\"text/javascript\">\n\t\t\tvar cmb2 = document.getElementById( 'cmb2-options-page-<?php echo $this->key; ?>' );\n\t\t\tvar submit = document.getElementById( 'submit' ).parentNode;\n\t\t\tsubmit.parentNode.insertBefore( cmb2, submit );\n\t\t\tcmb2.style.display = '';\n\t\t</script>\n\t\t<?php\n\t}\n\n\t/**\n\t * Public getter method for retrieving protected/private variables\n\t * @since  0.1.0\n\t * @param  string  $field Field to retrieve\n\t * @return mixed          Field value or exception is thrown\n\t */\n\tpublic function __get( $field ) {\n\t\t// Allowed fields to retrieve\n\t\tif ( in_array( $field, array( 'key', 'metabox_id', 'screen_id' ), true ) ) {\n\t\t\treturn $this->{$field};\n\t\t}\n\n\t\tthrow new Exception( 'Invalid property: ' . $field );\n\t}\n\n}\n\n/**\n * Helper function to get/return the Prefix_Add_CMB2_To_Settings_Page object\n * @since  0.1.0\n * @return Prefix_Add_CMB2_To_Settings_Page object\n */\nfunction myprefix_cmb2_on_settings() {\n\treturn Prefix_Add_CMB2_To_Settings_Page::get_instance();\n}\n\n\n/**\n * Wrapper function around cmb2_get_option\n * @since  0.1.0\n * @param  string $key     Options array key\n * @param  mixed  $default Optional default value\n * @return mixed           Option value\n */\nfunction myprefix_get_option( $key = '', $default = false ) {\n\tif ( function_exists( 'cmb2_get_option' ) ) {\n\t\t// Use cmb2_get_option as it passes through some key filters.\n\t\treturn cmb2_get_option( myprefix_cmb2_on_settings()->key, $key, $default );\n\t}\n\t// Fallback to get_option if CMB2 is not loaded yet.\n\t$opts = get_option( myprefix_cmb2_on_settings()->key, $default );\n\t$val = $default;\n\tif ( 'all' == $key ) {\n\t\t$val = $opts;\n\t} elseif ( is_array( $opts ) && array_key_exists( $key, $opts ) && false !== $opts[ $key ] ) {\n\t\t$val = $opts[ $key ];\n\t}\n\treturn $val;\n}\n\n// Get it started\nmyprefix_cmb2_on_settings();"
  },
  {
    "path": "options-and-settings-pages/custom-display-callback.php",
    "content": "<?php\nadd_action( 'cmb2_admin_init', 'yourprefix_register_theme_options_metabox' );\n/**\n * Hook in and register a metabox to handle a theme options page and adds a menu item.\n */\nfunction yourprefix_register_theme_options_metabox() {\n\n\t$cmb_options = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_theme_options_page',\n\t\t'title'        => 'Theme Options',\n\t\t'object_types' => array( 'options-page' ),\n\t\t'option_key'   => 'yourprefix_theme_options',\n\t\t'icon_url'     => 'dashicons-palmtree',\n\t\t'display_cb'   => 'yourprefix_theme_options_page_output', // Override the options-page form output (CMB2_Hookup::options_page_output()).\n\t\t'description'  => 'Custom description', // Will be displayed via our display_cb.\n\t) );\n\n\t$cmb_options->add_field( array(\n\t\t'name'    => 'Site Background Color',\n\t\t'desc'    => 'field description (optional)',\n\t\t'id'      => 'bg_color',\n\t\t'type'    => 'colorpicker',\n\t\t'default' => '#ffffff',\n\t) );\n\n}\n\nfunction yourprefix_theme_options_page_output( $hookup ) {\n\t// Output custom markup for the options-page.\n\t?>\n\t<div class=\"wrap cmb2-options-page option-<?php echo $hookup->option_key; ?>\">\n\t\t<?php if ( $hookup->cmb->prop( 'title' ) ) : ?>\n\t\t\t<h2><?php echo wp_kses_post( $hookup->cmb->prop( 'title' ) ); ?></h2>\n\t\t<?php endif; ?>\n\t\t<?php if ( $hookup->cmb->prop( 'description' ) ) : ?>\n\t\t\t<h2><?php echo wp_kses_post( $hookup->cmb->prop( 'description' ) ); ?></h2>\n\t\t<?php endif; ?>\n\t\t<form class=\"cmb-form\" action=\"<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>\" method=\"POST\" id=\"<?php echo $hookup->cmb->cmb_id; ?>\" enctype=\"multipart/form-data\" encoding=\"multipart/form-data\">\n\t\t\t<input type=\"hidden\" name=\"action\" value=\"<?php echo esc_attr( $hookup->option_key ); ?>\">\n\t\t\t<?php $hookup->options_page_metabox(); ?>\n\t\t\t<?php submit_button( esc_attr( $hookup->cmb->prop( 'save_button' ) ), 'primary', 'submit-cmb' ); ?>\n\t\t</form>\n\t</div>\n\t<?php\n}\n"
  },
  {
    "path": "options-and-settings-pages/genesis-cpt-archive-metabox.php",
    "content": "<?php\n\n/**\n * CMB2 Genesis CPT Archive Metabox\n *\n * To fetch these options, use `genesis_get_cpt_option()`, e.g.\n *    // In CPT archive template:\n *    if ( genesis_has_post_type_archive_support() ) {\n *        $color = genesis_get_cpt_option( 'test_colorpicker' );\n *    }\n *\n * @version 0.1.0\n */\nclass Myprefix_Genesis_CPT_Settings_Metabox {\n\n\t/**\n\t * Metabox id\n\t *\n\t * @var string\n\t */\n\tprotected $metabox_id = 'genesis-cpt-archive-settings-metabox-%1$s';\n\n\t/**\n\t * CPT slug\n\t *\n\t * @var string\n\t */\n\tprotected $post_type = '';\n\n\t/**\n\t * CPT slug\n\t *\n\t * @var string\n\t */\n\tprotected $admin_hook = '%1$s_page_genesis-cpt-archive-%1$s';\n\n\t/**\n\t * Option key, and option page slug\n\t *\n\t * @var string\n\t */\n\tprotected $key = 'genesis-cpt-archive-settings-%1$s';\n\n\t/**\n\t * Holds an instance of CMB2\n\t *\n\t * @var CMB2\n\t */\n\tprotected $cmb = null;\n\n\t/**\n\t * Holds all instances of this class.\n\t *\n\t * @var Myprefix_Genesis_CPT_Settings_Metabox\n\t */\n\tprotected static $instances = array();\n\n\t/**\n\t * Returns an instance.\n\t *\n\t * @since  0.1.0\n\t *\n\t * @param  string $post_type Post type slug.\n\t *\n\t * @return Myprefix_Genesis_CPT_Settings_Metabox\n\t */\n\tpublic static function get_instance( $post_type ) {\n\t\tif ( empty( self::$instances[ $post_type ] ) ) {\n\t\t\tself::$instances[ $post_type ] = new self( $post_type );\n\t\t\tself::$instances[ $post_type ]->hooks();\n\t\t}\n\n\t\treturn self::$instances[ $post_type ];\n\t}\n\n\t/**\n\t * Constructor\n\t *\n\t * @since 0.1.0\n\t *\n\t * @param string $post_type Post type slug.\n\t */\n\tprotected function __construct( $post_type ) {\n\t\t$this->post_type  = $post_type;\n\t\t$this->admin_hook = sprintf( $this->admin_hook, $post_type );\n\t\t$this->key        = sprintf( $this->key, $post_type );\n\t\t$this->metabox_id = sprintf( $this->metabox_id, $post_type );\n\t}\n\n\t/**\n\t * Initiate our hooks\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function hooks() {\n\t\tadd_action( 'init', array( $this, 'init' ) );\n\t\tadd_action( 'admin_menu', array( $this, 'admin_hooks' ) );\n\t\tadd_action( 'cmb2_admin_init', array( $this, 'init_metabox' ) );\n\t}\n\n\n\t/**\n\t * Initiate admin hooks.\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function init() {\n\t\t// Add custom archive support for CPT.\n\t\tadd_post_type_support( $this->post_type, 'genesis-cpt-archives-settings' );\n\t}\n\n\t/**\n\t * Add admin hooks.\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function admin_hooks() {\n\t\t// Include CMB CSS in the head to avoid FOUC.\n\t\tadd_action( \"admin_print_styles-{$this->admin_hook}\", array( 'CMB2_hookup', 'enqueue_cmb_css' ) );\n\n\t\t// Hook into the genesis cpt settings save and add in the CMB2 sanitized values.\n\t\tadd_filter( \"sanitize_option_genesis-cpt-archive-settings-{$this->post_type}\", array( $this, 'add_sanitized_values' ), 999 );\n\n\t\t// Hook up our Genesis metabox.\n\t\tadd_action( 'genesis_cpt_archives_settings_metaboxes', array( $this, 'add_meta_box' ) );\n\t}\n\n\t/**\n\t * Hook up our Genesis metabox.\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function add_meta_box() {\n\t\t$cmb = $this->init_metabox();\n\t\tadd_meta_box(\n\t\t\t$cmb->cmb_id,\n\t\t\t$cmb->prop( 'title' ),\n\t\t\tarray( $this, 'output_metabox' ),\n\t\t\t$this->admin_hook,\n\t\t\t$cmb->prop( 'context' ),\n\t\t\t$cmb->prop( 'priority' )\n\t\t);\n\t}\n\n\t/**\n\t * Output our Genesis metabox.\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function output_metabox() {\n\t\t$cmb = $this->init_metabox();\n\t\t$cmb->show_form( $cmb->object_id(), $cmb->object_type() );\n\t}\n\n\t/**\n\t * If saving the cpt settings option, add the CMB2 sanitized values.\n\t *\n\t * @since 0.1.0\n\t *\n\t * @param array $new_value Array of values for the setting.\n\t *\n\t * @return array Updated array of values for the setting.\n\t */\n\tpublic function add_sanitized_values( $new_value ) {\n\t\tif ( ! empty( $_POST ) ) {\n\t\t\t$cmb = $this->init_metabox();\n\n\t\t\t$new_value = array_merge(\n\t\t\t\t$new_value,\n\t\t\t\t$cmb->get_sanitized_values( $_POST )\n\t\t\t);\n\t\t}\n\n\t\treturn $new_value;\n\t}\n\n\t/**\n\t * Register our Genesis metabox and return the CMB2 instance.\n\t *\n\t * @since 0.1.0\n\t *\n\t * @return CMB2 instance.\n\t */\n\tpublic function init_metabox() {\n\t\tif ( null !== $this->cmb ) {\n\t\t\treturn $this->cmb;\n\t\t}\n\n\t\t$this->cmb = cmb2_get_metabox( array(\n\t\t\t'id'           => $this->metabox_id,\n\t\t\t'title'        => __( 'I\\'m a Genesis Archive Settings CMB2 metabox', 'myprefix' ),\n\t\t\t'hookup'       => false, // We'll handle ourselves. (add_sanitized_values())\n\t\t\t'cmb_styles'   => false, // We'll handle ourselves. (admin_hooks())\n\t\t\t'context'      => 'main', // Important for Genesis.\n\t\t\t// 'priority'     => 'low', // Defaults to 'high'.\n\t\t\t'object_types' => array( $this->admin_hook ),\n\t\t\t'show_on'      => array(\n\t\t\t\t// These are important, don't remove.\n\t\t\t\t'key'   => 'options-page',\n\t\t\t\t'value' => array( $this->key ),\n\t\t\t),\n\t\t), $this->key, 'options-page' );\n\n\t\t// Set our CMB2 fields.\n\t\t$this->cmb->add_field( array(\n\t\t\t'name' => __( 'Test Text', 'myprefix' ),\n\t\t\t'desc' => __( 'field description (optional)', 'myprefix' ),\n\t\t\t'id'   => 'test_text',\n\t\t\t'type' => 'text',\n\t\t\t// 'default' => 'Default Text',\n\t\t) );\n\n\t\t$this->cmb->add_field( array(\n\t\t\t'name'    => __( 'Test Color Picker', 'myprefix' ),\n\t\t\t'desc'    => __( 'field description (optional)', 'myprefix' ),\n\t\t\t'id'      => 'test_colorpicker',\n\t\t\t'type'    => 'colorpicker',\n\t\t\t'default' => '#bada55',\n\t\t) );\n\n\t\treturn $this->cmb;\n\t}\n\n\t/**\n\t * Public getter method for retrieving protected/private variables\n\t *\n\t * @since 0.1.0\n\t *\n\t * @param string $field Field to retrieve.\n\t *\n\t * @throws Exception Throws an exception if the field is invalid.\n\t *\n\t * @return mixed Field value or exception is thrown\n\t */\n\tpublic function __get( $field ) {\n\t\t// Allowed fields to retrieve.\n\t\tif ( 'cmb' === $field ) {\n\t\t\treturn $this->init_metabox();\n\t\t}\n\n\t\tif ( in_array( $field, array( 'metabox_id', 'post_type', 'admin_hook', 'key' ), true ) ) {\n\t\t\treturn $this->{$field};\n\t\t}\n\n\t\tthrow new Exception( 'Invalid property: ' . $field );\n\t}\n\n}\n\n/**\n * Helper function to get/return the Myprefix_Genesis_CPT_Settings_Metabox object.\n *\n * @since 0.1.0\n *\n * @param string $post_type Post type slug.\n *\n * @return Myprefix_Genesis_CPT_Settings_Metabox object\n */\nfunction myprefix_genesis_cpt_settings( $post_type ) {\n\treturn Myprefix_Genesis_CPT_Settings_Metabox::get_instance( $post_type );\n}\n\n// Get it started.\n// myprefix_genesis_cpt_settings( 'custom-post-type-slug' );\n"
  },
  {
    "path": "options-and-settings-pages/genesis-settings-metabox.php",
    "content": "<?php\n\n/**\n * CMB2 Genesis Settings Metabox\n *\n * To fetch these options, use `genesis_get_option()`, e.g.\n *      $color = genesis_get_option( 'test_colorpicker' );\n *\n * @version 0.1.0\n */\nclass Myprefix_Genesis_Settings_Metabox {\n\n\t/**\n\t * Option key. Could maybe be 'genesis-seo-settings', or other section?\n\t *\n\t * @var string\n\t */\n\tprotected $key = 'genesis-settings';\n\n\t/**\n\t * The admin page slug.\n\t *\n\t * @var string\n\t */\n\tprotected $admin_page = 'genesis';\n\n\t/**\n\t * Options page metabox id\n\t *\n\t * @var string\n\t */\n\tprotected $metabox_id = 'myprefix_genesis_settings';\n\n\t/**\n\t * Admin page hook\n\t *\n\t * @var string\n\t */\n\tprotected $admin_hook = 'toplevel_page_genesis';\n\n\t/**\n\t * Holds an instance of CMB2\n\t *\n\t * @var CMB2\n\t */\n\tprotected $cmb = null;\n\n\t/**\n\t * Holds an instance of the object\n\t *\n\t * @var Myprefix_Genesis_Settings_Metabox\n\t */\n\tprotected static $instance = null;\n\n\t/**\n\t * Returns the running object\n\t *\n\t * @return Myprefix_Genesis_Settings_Metabox\n\t */\n\tpublic static function get_instance() {\n\t\tif ( null === self::$instance ) {\n\t\t\tself::$instance = new self();\n\t\t\tself::$instance->hooks();\n\t\t}\n\n\t\treturn self::$instance;\n\t}\n\n\t/**\n\t * Constructor\n\t *\n\t * @since 0.1.0\n\t */\n\tprotected function __construct() {\n\t}\n\n\t/**\n\t * Initiate our hooks\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function hooks() {\n\t\tadd_action( 'admin_menu', array( $this, 'admin_hooks' ) );\n\t\tadd_action( 'cmb2_admin_init', array( $this, 'init_metabox' ) );\n\t}\n\n\t/**\n\t * Add menu options page\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function admin_hooks() {\n\t\t// Include CMB CSS in the head to avoid FOUC.\n\t\tadd_action( \"admin_print_styles-{$this->admin_hook}\", array( 'CMB2_hookup', 'enqueue_cmb_css' ) );\n\n\t\t// Hook into the genesis cpt setttings save and add in the CMB2 sanitized values.\n\t\tadd_filter( \"sanitize_option_{$this->key}\", array( $this, 'add_sanitized_values' ), 999 );\n\n\t\t// Hook up our Genesis metabox.\n\t\tadd_action( 'genesis_theme_settings_metaboxes', array( $this, 'add_meta_box' ) );\n\t}\n\n\n\t/**\n\t * Hook up our Genesis metabox.\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function add_meta_box() {\n\t\t$cmb = $this->init_metabox();\n\t\tadd_meta_box(\n\t\t\t$cmb->cmb_id,\n\t\t\t$cmb->prop( 'title' ),\n\t\t\tarray( $this, 'output_metabox' ),\n\t\t\t$this->admin_hook,\n\t\t\t$cmb->prop( 'context' ),\n\t\t\t$cmb->prop( 'priority' )\n\t\t);\n\t}\n\n\t/**\n\t * Output our Genesis metabox.\n\t *\n\t * @since 0.1.0\n\t */\n\tpublic function output_metabox() {\n\t\t$cmb = $this->init_metabox();\n\t\t$cmb->show_form( $cmb->object_id(), $cmb->object_type() );\n\t}\n\n\t/**\n\t * If saving the cpt settings option, add the CMB2 sanitized values.\n\t *\n\t * @since 0.1.0\n\t *\n\t * @param array $new_value Array of values for the setting.\n\t *\n\t * @return array Updated array of values for the setting.\n\t */\n\tpublic function add_sanitized_values( $new_value ) {\n\t\tif ( ! empty( $_POST ) ) {\n\t\t\t$cmb = $this->init_metabox();\n\n\t\t\t$new_value = array_merge(\n\t\t\t\t$new_value,\n\t\t\t\t$cmb->get_sanitized_values( $_POST )\n\t\t\t);\n\t\t}\n\n\t\treturn $new_value;\n\t}\n\n\t/**\n\t * Register our Genesis metabox and return the CMB2 instance.\n\t *\n\t * @since  0.1.0\n\t *\n\t * @return CMB2 instance.\n\t */\n\tpublic function init_metabox() {\n\t\tif ( null !== $this->cmb ) {\n\t\t\treturn $this->cmb;\n\t\t}\n\n\t\t$this->cmb = cmb2_get_metabox( array(\n\t\t\t'id'           => $this->metabox_id,\n\t\t\t'title'        => __( 'I\\'m a Genesis Settings CMB2 metabox', 'myprefix' ),\n\t\t\t'hookup'       => false, // We'll handle ourselves. (add_sanitized_values())\n\t\t\t'cmb_styles'   => false, // We'll handle ourselves. (admin_hooks())\n\t\t\t'context'      => 'main', // Important for Genesis.\n\t\t\t// 'priority'     => 'low', // Defaults to 'high'.\n\t\t\t'object_types' => array( $this->admin_hook ),\n\t\t\t'show_on'      => array(\n\t\t\t\t// These are important, don't remove.\n\t\t\t\t'key'   => 'options-page',\n\t\t\t\t'value' => array( $this->key ),\n\t\t\t),\n\t\t), $this->key, 'options-page' );\n\n\t\t// Set our CMB2 fields.\n\t\t$this->cmb->add_field( array(\n\t\t\t'name' => __( 'Test Text', 'myprefix' ),\n\t\t\t'desc' => __( 'field description (optional)', 'myprefix' ),\n\t\t\t'id'   => 'test_text',\n\t\t\t'type' => 'text',\n\t\t\t// 'default' => 'Default Text',\n\t\t) );\n\n\t\t$this->cmb->add_field( array(\n\t\t\t'name'    => __( 'Test Color Picker', 'myprefix' ),\n\t\t\t'desc'    => __( 'field description (optional)', 'myprefix' ),\n\t\t\t'id'      => 'test_colorpicker',\n\t\t\t'type'    => 'colorpicker',\n\t\t\t'default' => '#bada55',\n\t\t) );\n\n\t\treturn $this->cmb;\n\t}\n\n\t/**\n\t * Public getter method for retrieving protected/private variables.\n\t *\n\t * @since 0.1.0\n\t *\n\t * @param string $field Field to retrieve.\n\t *\n\t * @throws Exception Throws an exception if the field is invalid.\n\t *\n\t * @return mixed Field value or exception is thrown\n\t */\n\tpublic function __get( $field ) {\n\t\tif ( 'cmb' === $field ) {\n\t\t\treturn $this->init_metabox();\n\t\t}\n\n\t\t// Allowed fields to retrieve.\n\t\tif ( in_array( $field, array( 'key', 'admin_page', 'metabox_id', 'admin_hook' ), true ) ) {\n\t\t\treturn $this->{$field};\n\t\t}\n\n\t\tthrow new Exception( 'Invalid property: ' . $field );\n\t}\n\n}\n\n/**\n * Helper function to get/return the Myprefix_Genesis_Settings_Metabox object\n *\n * @since 0.1.0\n *\n * @return Myprefix_Genesis_Settings_Metabox object\n */\nfunction myprefix_genesis_settings_metabox() {\n\treturn Myprefix_Genesis_Settings_Metabox::get_instance();\n}\n\n// Get it started.\nmyprefix_genesis_settings_metabox();\n"
  },
  {
    "path": "options-and-settings-pages/network-options-cmb.php",
    "content": "<?php\n/**\n * This snippet has been updated to reflect the official supporting of options pages by CMB2\n * in version 2.2.5.\n *\n * If you are using the old version of the network options-page registration,\n * it is recommended you swtich to this method.\n */\nadd_action( 'cmb2_admin_init', 'myprefix_register_network_options_metabox' );\n/**\n * Hook in and register a metabox to handle a theme options page and adds a menu item.\n */\nfunction myprefix_register_network_options_metabox() {\n\n\t/**\n\t * Registers options page menu item and form.\n\t */\n\t$cmb_options = new_cmb2_box( array(\n\t\t'id'           => 'myprefix_network_option_metabox',\n\t\t'title'        => esc_html__( 'Network Setting', 'myprefix' ),\n\t\t'object_types' => array( 'options-page' ),\n\n\t\t/*\n\t\t * The following parameters are specific to the options-page box\n\t\t * Several of these parameters are passed along to add_menu_page()/add_submenu_page().\n\t\t */\n\n\t\t'option_key'      => 'myprefix_network_options', // The option key and admin menu page slug.\n\t\t// 'icon_url'        => 'dashicons-palmtree', // Menu icon. Only applicable if 'parent_slug' is left empty.\n\t\t// 'menu_title'      => esc_html__( 'Options', 'myprefix' ), // Falls back to 'title' (above).\n\t\t// 'parent_slug'     => 'themes.php', // Make options page a submenu item of the themes menu.\n\t\t// 'capability'      => 'manage_options', // Cap required to view options-page.\n\t\t// 'position'        => 1, // Menu position. Only applicable if 'parent_slug' is left empty.\n\t\t'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page.\n\t\t// 'display_cb'      => false, // Override the options-page form output (CMB2_Hookup::options_page_output()).\n\t\t// 'save_button'     => esc_html__( 'Save Theme Options', 'myprefix' ), // The text for the options-page save button. Defaults to 'Save'.\n\t) );\n\n\t/*\n\t * Options fields ids only need\n\t * to be unique within this box.\n\t * Prefix is not needed.\n\t */\n\n\t$cmb_options->add_field( array(\n\t\t'name' => __( 'Test Text', 'myprefix' ),\n\t\t'desc' => __( 'field description (optional)', 'myprefix' ),\n\t\t'id'   => 'test_text',\n\t\t'type' => 'text',\n\t\t'default' => 'Default Text',\n\t) );\n\n\t$cmb_options->add_field( array(\n\t\t'name'    => __( 'Test Color Picker', 'myprefix' ),\n\t\t'desc'    => __( 'field description (optional)', 'myprefix' ),\n\t\t'id'      => 'test_colorpicker',\n\t\t'type'    => 'colorpicker',\n\t\t'default' => '#bada55',\n\t) );\n\n\n}\n\n/**\n * Wrapper function around cmb2_get_option\n * @since  0.1.0\n * @param  string $key     Options array key\n * @param  mixed  $default Optional default value\n * @return mixed           Option value\n */\nfunction myprefix_get_network_option( $key = '', $default = false ) {\n\tif ( function_exists( 'cmb2_get_option' ) ) {\n\t\t// Use cmb2_get_option as it passes through some key filters.\n\t\treturn cmb2_get_option( 'myprefix_network_options', $key, $default );\n\t}\n\n\t// Fallback to get_site_option if CMB2 is not loaded yet.\n\t$opts = get_site_option( 'myprefix_network_options', $default );\n\n\t$val = $default;\n\n\tif ( 'all' == $key ) {\n\t\t$val = $opts;\n\t} elseif ( is_array( $opts ) && array_key_exists( $key, $opts ) && false !== $opts[ $key ] ) {\n\t\t$val = $opts[ $key ];\n\t}\n\n\treturn $val;\n}\n"
  },
  {
    "path": "options-and-settings-pages/non-cmb2-options-page.php",
    "content": "<?php\n/**\n * Adding standard WP Settings API options page as sub-menu item to CMB2 Tabs.\n *\n * @see https://developer.wordpress.org/plugins/settings/custom-settings-page/\n *\n * @internal never define functions inside callbacks.\n * these functions could be run multiple times; this would result in a fatal error.\n */\n\n/**\n * custom option and settings\n */\nfunction wporg_settings_init() {\n\t// Register a new setting for \"wporg\" page.\n\tregister_setting( 'wporg', 'wporg_options' );\n\n\t// Register a new section in the \"wporg\" page.\n\tadd_settings_section(\n\t\t'wporg_section_developers',\n\t\t__( 'The Matrix has you.', 'wporg' ), 'wporg_section_developers_callback',\n\t\t'wporg'\n\t);\n\n\t// Register a new field in the \"wporg_section_developers\" section, inside the \"wporg\" page.\n\tadd_settings_field(\n\t\t'wporg_field_pill', // As of WP 4.6 this value is used only internally.\n\t\t// Use $args' label_for to populate the id inside the callback.\n\t\t__( 'Pill', 'wporg' ),\n\t\t'wporg_field_pill_cb',\n\t\t'wporg',\n\t\t'wporg_section_developers',\n\t\tarray(\n\t\t\t'label_for'         => 'wporg_field_pill',\n\t\t\t'class'             => 'wporg_row',\n\t\t\t'wporg_custom_data' => 'custom',\n\t\t)\n\t);\n}\n\n/**\n * Register our wporg_settings_init to the admin_init action hook.\n */\nadd_action( 'admin_init', 'wporg_settings_init' );\n\n\n/**\n * Custom option and settings:\n *  - callback functions\n */\n\n\n/**\n * Developers section callback function.\n *\n * @param array $args  The settings array, defining title, id, callback.\n */\nfunction wporg_section_developers_callback( $args ) {\n\t?>\n\t<p id=\"<?php echo esc_attr( $args['id'] ); ?>\"><?php esc_html_e( 'Follow the white rabbit.', 'wporg' ); ?></p>\n\t<?php\n}\n\n/**\n * Pill field callbakc function.\n *\n * WordPress has magic interaction with the following keys: label_for, class.\n * - the \"label_for\" key value is used for the \"for\" attribute of the <label>.\n * - the \"class\" key value is used for the \"class\" attribute of the <tr> containing the field.\n * Note: you can add custom key value pairs to be used inside your callbacks.\n *\n * @param array $args\n */\nfunction wporg_field_pill_cb( $args ) {\n\t// Get the value of the setting we've registered with register_setting()\n\t$options = get_option( 'wporg_options' );\n\t?>\n\t<select\n\t\tid=\"<?php echo esc_attr( $args['label_for'] ); ?>\"\n\t\tdata-custom=\"<?php echo esc_attr( $args['wporg_custom_data'] ); ?>\"\n\t\tname=\"wporg_options[<?php echo esc_attr( $args['label_for'] ); ?>]\"\n\t>\n\t\t<option value=\"red\" <?php echo isset( $options[ $args['label_for'] ] ) ? ( selected( $options[ $args['label_for'] ], 'red', false ) ) : ( '' ); ?>>\n\t\t<?php esc_html_e( 'red pill', 'wporg' ); ?>\n\t\t</option>\n\t\t<option value=\"blue\" <?php echo isset( $options[ $args['label_for'] ] ) ? ( selected( $options[ $args['label_for'] ], 'blue', false ) ) : ( '' ); ?>>\n\t\t<?php esc_html_e( 'blue pill', 'wporg' ); ?>\n\t\t</option>\n\t</select>\n\t<p class=\"description\">\n\t\t<?php esc_html_e( 'You take the blue pill and the story ends. You wake in your bed and you believe whatever you want to believe.', 'wporg' ); ?>\n\t</p>\n\t<p class=\"description\">\n\t\t<?php esc_html_e( 'You take the red pill and you stay in Wonderland and I show you how deep the rabbit-hole goes.', 'wporg' ); ?>\n\t</p>\n\t<?php\n}\n\n/**\n * Add the top level menu page.\n */\nfunction wporg_options_page() {\n\tadd_submenu_page(\n\t\t'yourprefix_theme_options',\n\t\t'WPOrg',\n\t\t'WPOrg Options',\n\t\t'manage_options',\n\t\t'wporg',\n\t\t'wporg_options_page_html'\n\t);\n}\n\n\n/**\n * Register our wporg_options_page to the admin_menu action hook.\n */\nadd_action( 'admin_menu', 'wporg_options_page' );\n\n\n/**\n * Top level menu callback function\n */\nfunction wporg_options_page_html() {\n\t// check user capabilities\n\tif ( ! current_user_can( 'manage_options' ) ) {\n\t\treturn;\n\t}\n\n\t// add error/update messages\n\n\t// check if the user have submitted the settings\n\t// WordPress will add the \"settings-updated\" $_GET parameter to the url\n\tif ( isset( $_GET['settings-updated'] ) ) {\n\t\t// add settings saved message with the class of \"updated\"\n\t\tadd_settings_error( 'wporg_messages', 'wporg_message', __( 'Settings Saved', 'wporg' ), 'updated' );\n\t}\n\n\t$cmb    = cmb2_get_metabox( 'yourprefix_theme_options_page' );\n\t$hookup = new CMB2_Options_Hookup( $cmb, 'yourprefix_theme_options' );\n\n\t// show error/update messages\n\tsettings_errors( 'wporg_messages' );\n\t?>\n\t<div class=\"wrap\">\n\t\t<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>\n\t\t<?php $hookup->options_page_tab_nav_output(); ?>\n\t\t<form action=\"options.php\" method=\"post\">\n\t\t\t<?php\n\t\t\t// output security fields for the registered setting \"wporg\"\n\t\t\tsettings_fields( 'wporg' );\n\t\t\t// output setting sections and their fields\n\t\t\t// (sections are registered for \"wporg\", each field is registered to a specific section)\n\t\t\tdo_settings_sections( 'wporg' );\n\t\t\t// output save settings button\n\t\t\tsubmit_button( 'Save Settings' );\n\t\t\t?>\n\t\t</form>\n\t</div>\n\t<?php\n}\n\nfunction wporg_options_page_to_cmb2_tabs( $tabs, $tab_group ) {\n\tif ( 'yourprefix_theme_options' === $tab_group ) {\n\t\t$tabs['wporg'] = 'WPOrg';\n\t}\n\n\treturn $tabs;\n}\n\nadd_filter( 'cmb2_tab_group_tabs', 'wporg_options_page_to_cmb2_tabs', 10, 2 );\n"
  },
  {
    "path": "options-and-settings-pages/options-pages-with-submenus.php",
    "content": "<?php\n\n/**\n * Hook in and register a metabox to handle a theme options page and adds a menu item.\n */\nfunction yourprefix_register_main_options_metabox() {\n\n\t/**\n\t * Registers main options page menu item and form.\n\t */\n\t$main_options = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_main_options_page',\n\t\t'title'        => esc_html__( 'Main Options', 'cmb2' ),\n\t\t'object_types' => array( 'options-page' ),\n\n\t\t/*\n\t\t * The following parameters are specific to the options-page box\n\t\t * Several of these parameters are passed along to add_menu_page()/add_submenu_page().\n\t\t */\n\n\t\t'option_key'      => 'yourprefix_main_options', // The option key and admin menu page slug.\n\t\t// 'icon_url'        => 'dashicons-palmtree', // Menu icon. Only applicable if 'parent_slug' is left empty.\n\t\t// 'menu_title'      => esc_html__( 'Options', 'cmb2' ), // Falls back to 'title' (above).\n\t\t// 'parent_slug'     => 'themes.php', // Make options page a submenu item of the themes menu.\n\t\t// 'capability'      => 'manage_options', // Cap required to view options-page.\n\t\t// 'position'        => 1, // Menu position. Only applicable if 'parent_slug' is left empty.\n\t\t// 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page.\n\t\t// 'display_cb'      => false, // Override the options-page form output (CMB2_Hookup::options_page_output()).\n\t\t// 'save_button'     => esc_html__( 'Save Theme Options', 'cmb2' ), // The text for the options-page save button. Defaults to 'Save'.\n\t\t// 'disable_settings_errors' => true, // On settings pages (not options-general.php sub-pages), allows disabling.\n\t\t// 'message_cb'      => 'yourprefix_options_page_message_callback',\n\t) );\n\n\t/**\n\t * Options fields ids only need\n\t * to be unique within this box.\n\t * Prefix is not needed.\n\t */\n\t$main_options->add_field( array(\n\t\t'name'    => esc_html__( 'Site Background Color', 'cmb2' ),\n\t\t'desc'    => esc_html__( 'field description (optional)', 'cmb2' ),\n\t\t'id'      => 'bg_color',\n\t\t'type'    => 'colorpicker',\n\t\t'default' => '#ffffff',\n\t) );\n\n\t/**\n\t * Registers secondary options page, and set main item as parent.\n\t */\n\t$secondary_options = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_secondary_options_page',\n\t\t'title'        => esc_html__( 'Secondary Options', 'cmb2' ),\n\t\t'object_types' => array( 'options-page' ),\n\t\t'option_key'   => 'yourprefix_secondary_options',\n\t\t'parent_slug'  => 'yourprefix_main_options',\n\t) );\n\n\t$secondary_options->add_field( array(\n\t\t'name'    => esc_html__( 'Test Radio', 'cmb2' ),\n\t\t'desc'    => esc_html__( 'field description (optional)', 'cmb2' ),\n\t\t'id'      => 'radio',\n\t\t'type'    => 'radio',\n\t\t'options' => array(\n\t\t\t'option1' => esc_html__( 'Option One', 'cmb2' ),\n\t\t\t'option2' => esc_html__( 'Option Two', 'cmb2' ),\n\t\t\t'option3' => esc_html__( 'Option Three', 'cmb2' ),\n\t\t),\n\t) );\n\n\t/**\n\t * Registers tertiary options page, and set main item as parent.\n\t */\n\t$tertiary_options = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_tertiary_options_page',\n\t\t'title'        => esc_html__( 'Tertiary Options', 'cmb2' ),\n\t\t'object_types' => array( 'options-page' ),\n\t\t'option_key'   => 'yourprefix_tertiary_options',\n\t\t'parent_slug'  => 'yourprefix_main_options',\n\t) );\n\n\t$tertiary_options->add_field( array(\n\t\t'name' => esc_html__( 'Test Text Area for Code', 'cmb2' ),\n\t\t'desc' => esc_html__( 'field description (optional)', 'cmb2' ),\n\t\t'id'   => 'textarea_code',\n\t\t'type' => 'textarea_code',\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'yourprefix_register_main_options_metabox' );\n"
  },
  {
    "path": "options-and-settings-pages/options-pages-with-tabs-and-submenus.php",
    "content": "<?php\n/**\n * Hook in and register a metabox to handle a theme options page and adds a menu item.\n */\nfunction yourprefix_register_main_options_metabox() {\n\n\t/**\n\t * Registers main options page menu item and form.\n\t */\n\t$args = array(\n\t\t'id'           => 'yourprefix_main_options_page',\n\t\t'title'        => 'Main Options',\n\t\t'object_types' => array( 'options-page' ),\n\t\t'option_key'   => 'yourprefix_main_options',\n\t\t'tab_group'    => 'yourprefix_main_options',\n\t\t'tab_title'    => 'Main',\n\t);\n\n\t// 'tab_group' property is supported in > 2.4.0.\n\tif ( version_compare( CMB2_VERSION, '2.4.0' ) ) {\n\t\t$args['display_cb'] = 'yourprefix_options_display_with_tabs';\n\t}\n\n\t$main_options = new_cmb2_box( $args );\n\n\t/**\n\t * Options fields ids only need\n\t * to be unique within this box.\n\t * Prefix is not needed.\n\t */\n\t$main_options->add_field( array(\n\t\t'name'    => 'Site Background Color',\n\t\t'desc'    => 'field description (optional)',\n\t\t'id'      => 'bg_color',\n\t\t'type'    => 'colorpicker',\n\t\t'default' => '#ffffff',\n\t) );\n\n\t/**\n\t * Registers secondary options page, and set main item as parent.\n\t */\n\t$args = array(\n\t\t'id'           => 'yourprefix_secondary_options_page',\n\t\t'menu_title'   => 'Secondary Options', // Use menu title, & not title to hide main h2.\n\t\t'object_types' => array( 'options-page' ),\n\t\t'option_key'   => 'yourprefix_secondary_options',\n\t\t'parent_slug'  => 'yourprefix_main_options',\n\t\t'tab_group'    => 'yourprefix_main_options',\n\t\t'tab_title'    => 'Secondary',\n\t);\n\n\t// 'tab_group' property is supported in > 2.4.0.\n\tif ( version_compare( CMB2_VERSION, '2.4.0' ) ) {\n\t\t$args['display_cb'] = 'yourprefix_options_display_with_tabs';\n\t}\n\n\t$secondary_options = new_cmb2_box( $args );\n\n\t$secondary_options->add_field( array(\n\t\t'name'    => 'Test Radio',\n\t\t'desc'    => 'field description (optional)',\n\t\t'id'      => 'radio',\n\t\t'type'    => 'radio',\n\t\t'options' => array(\n\t\t\t'option1' => 'Option One',\n\t\t\t'option2' => 'Option Two',\n\t\t\t'option3' => 'Option Three',\n\t\t),\n\t) );\n\n\t/**\n\t * Registers tertiary options page, and set main item as parent.\n\t */\n\t$args = array(\n\t\t'id'           => 'yourprefix_tertiary_options_page',\n\t\t'menu_title'   => 'Tertiary Options', // Use menu title, & not title to hide main h2.\n\t\t'object_types' => array( 'options-page' ),\n\t\t'option_key'   => 'yourprefix_tertiary_options',\n\t\t'parent_slug'  => 'yourprefix_main_options',\n\t\t'tab_group'    => 'yourprefix_main_options',\n\t\t'tab_title'    => 'Tertiary',\n\t);\n\n\t// 'tab_group' property is supported in > 2.4.0.\n\tif ( version_compare( CMB2_VERSION, '2.4.0' ) ) {\n\t\t$args['display_cb'] = 'yourprefix_options_display_with_tabs';\n\t}\n\n\t$tertiary_options = new_cmb2_box( $args );\n\n\t$tertiary_options->add_field( array(\n\t\t'name' => 'Test Text Area for Code',\n\t\t'desc' => 'field description (optional)',\n\t\t'id'   => 'textarea_code',\n\t\t'type' => 'textarea_code',\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'yourprefix_register_main_options_metabox' );\n\n/**\n * A CMB2 options-page display callback override which adds tab navigation among\n * CMB2 options pages which share this same display callback.\n *\n * @param CMB2_Options_Hookup $cmb_options The CMB2_Options_Hookup object.\n */\nfunction yourprefix_options_display_with_tabs( $cmb_options ) {\n\t$tabs = yourprefix_options_page_tabs( $cmb_options );\n\t?>\n\t<div class=\"wrap cmb2-options-page option-<?php echo $cmb_options->option_key; ?>\">\n\t\t<?php if ( get_admin_page_title() ) : ?>\n\t\t\t<h2><?php echo wp_kses_post( get_admin_page_title() ); ?></h2>\n\t\t<?php endif; ?>\n\t\t<h2 class=\"nav-tab-wrapper\">\n\t\t\t<?php foreach ( $tabs as $option_key => $tab_title ) : ?>\n\t\t\t\t<a class=\"nav-tab<?php if ( isset( $_GET['page'] ) && $option_key === $_GET['page'] ) : ?> nav-tab-active<?php endif; ?>\" href=\"<?php menu_page_url( $option_key ); ?>\"><?php echo wp_kses_post( $tab_title ); ?></a>\n\t\t\t<?php endforeach; ?>\n\t\t</h2>\n\t\t<form class=\"cmb-form\" action=\"<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>\" method=\"POST\" id=\"<?php echo $cmb_options->cmb->cmb_id; ?>\" enctype=\"multipart/form-data\" encoding=\"multipart/form-data\">\n\t\t\t<input type=\"hidden\" name=\"action\" value=\"<?php echo esc_attr( $cmb_options->option_key ); ?>\">\n\t\t\t<?php $cmb_options->options_page_metabox(); ?>\n\t\t\t<?php submit_button( esc_attr( $cmb_options->cmb->prop( 'save_button' ) ), 'primary', 'submit-cmb' ); ?>\n\t\t</form>\n\t</div>\n\t<?php\n}\n\n/**\n * Gets navigation tabs array for CMB2 options pages which share the given\n * display_cb param.\n *\n * @param CMB2_Options_Hookup $cmb_options The CMB2_Options_Hookup object.\n *\n * @return array Array of tab information.\n */\nfunction yourprefix_options_page_tabs( $cmb_options ) {\n\t$tab_group = $cmb_options->cmb->prop( 'tab_group' );\n\t$tabs      = array();\n\n\tforeach ( CMB2_Boxes::get_all() as $cmb_id => $cmb ) {\n\t\tif ( $tab_group === $cmb->prop( 'tab_group' ) ) {\n\t\t\t$tabs[ $cmb->options_page_keys()[0] ] = $cmb->prop( 'tab_title' )\n\t\t\t\t? $cmb->prop( 'tab_title' )\n\t\t\t\t: $cmb->prop( 'title' );\n\t\t}\n\t}\n\n\treturn $tabs;\n}\n"
  },
  {
    "path": "options-and-settings-pages/submenu-options-pages.php",
    "content": "<?php\n\n/**\n * Hook in and register a submenu options page for the Page post-type menu.\n */\nfunction yourprefix_register_options_submenu_for_page_post_type() {\n\n\t/**\n\t * Registers options page menu item and form.\n\t */\n\t$cmb = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_options_submenu_page',\n\t\t'title'        => esc_html__( 'Page Options', 'cmb2' ),\n\t\t'object_types' => array( 'options-page' ),\n\n\t\t/*\n\t\t * The following parameters are specific to the options-page box\n\t\t * Several of these parameters are passed along to add_menu_page()/add_submenu_page().\n\t\t */\n\n\t\t'option_key'      => 'yourprefix_page_options', // The option key and admin menu page slug.\n\t\t// 'icon_url'        => '', // Menu icon. Only applicable if 'parent_slug' is left empty.\n\t\t// 'menu_title'      => esc_html__( 'Options', 'cmb2' ), // Falls back to 'title' (above).\n\t\t'parent_slug'     => 'edit.php?post_type=page', // Make options page a submenu item of the themes menu.\n\t\t// 'capability'      => 'manage_options', // Cap required to view options-page.\n\t\t// 'position'        => 1, // Menu position. Only applicable if 'parent_slug' is left empty.\n\t\t// 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page.\n\t\t// 'display_cb'      => false, // Override the options-page form output (CMB2_Hookup::options_page_output()).\n\t\t// 'save_button'     => esc_html__( 'Save Theme Options', 'cmb2' ), // The text for the options-page save button. Defaults to 'Save'.\n\t\t// 'disable_settings_errors' => true, // On settings pages (not options-general.php sub-pages), allows disabling.\n\t\t// 'message_cb'      => 'yourprefix_options_page_message_callback',\n\t) );\n\n\t$cmb->add_field( array(\n\t\t'name'    => esc_html__( 'Background Color for Pages', 'cmb2' ),\n\t\t'desc'    => esc_html__( 'field description (optional)', 'cmb2' ),\n\t\t'id'      => 'bg_color',\n\t\t'type'    => 'colorpicker',\n\t\t'default' => '#ffffff',\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'yourprefix_register_options_submenu_for_page_post_type' );\n\n/**\n * Hook in and register a submenu options page for the Appearance menu.\n */\nfunction yourprefix_register_options_submenu_appearance_menu() {\n\n\t/**\n\t * Registers options page menu item and form.\n\t */\n\t$cmb_options = new_cmb2_box( array(\n\t\t'id'           => 'yourprefix_options_submenu_appearance_menu',\n\t\t'title'        => esc_html__( 'Appearance Options', 'cmb2' ),\n\t\t'object_types' => array( 'options-page' ),\n\n\t\t/*\n\t\t * The following parameters are specific to the options-page box\n\t\t * Several of these parameters are passed along to add_menu_page()/add_submenu_page().\n\t\t */\n\n\t\t'option_key'      => 'yourprefix_theme_appearance_options', // The option key and admin menu page slug.\n\t\t// 'icon_url'        => '', // Menu icon. Only applicable if 'parent_slug' is left empty.\n\t\t// 'menu_title'      => esc_html__( 'Options', 'cmb2' ), // Falls back to 'title' (above).\n\t\t'parent_slug'     => 'themes.php', // Make options page a submenu item of the themes menu.\n\t\t// 'capability'      => 'manage_options', // Cap required to view options-page.\n\t\t// 'position'        => 1, // Menu position. Only applicable if 'parent_slug' is left empty.\n\t\t// 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page.\n\t\t// 'display_cb'      => false, // Override the options-page form output (CMB2_Hookup::options_page_output()).\n\t\t// 'save_button'     => esc_html__( 'Save Theme Options', 'cmb2' ), // The text for the options-page save button. Defaults to 'Save'.\n\t\t// 'disable_settings_errors' => true, // On settings pages (not options-general.php sub-pages), allows disabling.\n\t\t// 'message_cb'      => 'yourprefix_options_page_message_callback',\n\t) );\n\n\t/**\n\t * Options fields ids only need\n\t * to be unique within this box.\n\t * Prefix is not needed.\n\t */\n\t$cmb_options->add_field( array(\n\t\t'name'    => esc_html__( 'Site Background Color', 'cmb2' ),\n\t\t'desc'    => esc_html__( 'field description (optional)', 'cmb2' ),\n\t\t'id'      => 'bg_color',\n\t\t'type'    => 'colorpicker',\n\t\t'default' => '#ffffff',\n\t) );\n\n}\nadd_action( 'cmb2_admin_init', 'yourprefix_register_options_submenu_appearance_menu' );\n"
  },
  {
    "path": "options-and-settings-pages/theme-options-cmb.php",
    "content": "<?php\n/**\n * This snippet has been updated to reflect the official supporting of options pages by CMB2\n * in version 2.2.5.\n *\n * If you are using the old version of the options-page registration,\n * it is recommended you swtich to this method.\n */\nadd_action( 'cmb2_admin_init', 'myprefix_register_theme_options_metabox' );\n/**\n * Hook in and register a metabox to handle a theme options page and adds a menu item.\n */\nfunction myprefix_register_theme_options_metabox() {\n\n\t/**\n\t * Registers options page menu item and form.\n\t */\n\t$cmb_options = new_cmb2_box( array(\n\t\t'id'           => 'myprefix_option_metabox',\n\t\t'title'        => esc_html__( 'Site Options', 'myprefix' ),\n\t\t'object_types' => array( 'options-page' ),\n\n\t\t/*\n\t\t * The following parameters are specific to the options-page box\n\t\t * Several of these parameters are passed along to add_menu_page()/add_submenu_page().\n\t\t */\n\n\t\t'option_key'      => 'myprefix_options', // The option key and admin menu page slug.\n\t\t// 'icon_url'        => 'dashicons-palmtree', // Menu icon. Only applicable if 'parent_slug' is left empty.\n\t\t// 'menu_title'      => esc_html__( 'Options', 'myprefix' ), // Falls back to 'title' (above).\n\t\t// 'parent_slug'     => 'themes.php', // Make options page a submenu item of the themes menu.\n\t\t// 'capability'      => 'manage_options', // Cap required to view options-page.\n\t\t// 'position'        => 1, // Menu position. Only applicable if 'parent_slug' is left empty.\n\t\t// 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page.\n\t\t// 'display_cb'      => false, // Override the options-page form output (CMB2_Hookup::options_page_output()).\n\t\t// 'save_button'     => esc_html__( 'Save Theme Options', 'myprefix' ), // The text for the options-page save button. Defaults to 'Save'.\n\t) );\n\n\t/*\n\t * Options fields ids only need\n\t * to be unique within this box.\n\t * Prefix is not needed.\n\t */\n\n\t$cmb_options->add_field( array(\n\t\t'name' => __( 'Test Text', 'myprefix' ),\n\t\t'desc' => __( 'field description (optional)', 'myprefix' ),\n\t\t'id'   => 'test_text',\n\t\t'type' => 'text',\n\t\t'default' => 'Default Text',\n\t) );\n\n\t$cmb_options->add_field( array(\n\t\t'name'    => __( 'Test Color Picker', 'myprefix' ),\n\t\t'desc'    => __( 'field description (optional)', 'myprefix' ),\n\t\t'id'      => 'test_colorpicker',\n\t\t'type'    => 'colorpicker',\n\t\t'default' => '#bada55',\n\t) );\n\n}\n\n/**\n * Wrapper function around cmb2_get_option\n * @since  0.1.0\n * @param  string $key     Options array key\n * @param  mixed  $default Optional default value\n * @return mixed           Option value\n */\nfunction myprefix_get_option( $key = '', $default = false ) {\n\tif ( function_exists( 'cmb2_get_option' ) ) {\n\t\t// Use cmb2_get_option as it passes through some key filters.\n\t\treturn cmb2_get_option( 'myprefix_options', $key, $default );\n\t}\n\n\t// Fallback to get_option if CMB2 is not loaded yet.\n\t$opts = get_option( 'myprefix_options', $default );\n\n\t$val = $default;\n\n\tif ( 'all' == $key ) {\n\t\t$val = $opts;\n\t} elseif ( is_array( $opts ) && array_key_exists( $key, $opts ) && false !== $opts[ $key ] ) {\n\t\t$val = $opts[ $key ];\n\t}\n\n\treturn $val;\n}\n"
  },
  {
    "path": "user-meta-and-settings/README.md",
    "content": "User Meta and Settings\n==========\n\nThese are examples of [using CMB2 to generate user fields](https://github.com/WebDevStudios/CMB2/wiki/Adding-metaboxes-to-user-profile)."
  },
  {
    "path": "widgets/widget-example.php",
    "content": "<?php\n/**\n * Plugin Name: CMB2 Widget Boilerplate\n * Description: A boilerplate for building widgets with CMB2. Early alpha version. NEEDS WORK.\n */\n\n// Exit if accessed directly\nif ( ! defined ( 'ABSPATH' ) ) {\n\texit;\n}\n\n/**\n * @todo Properly hook in JS events, etc. Fields which require JS are not working.\n * @todo Fix css styling. Probably needs a sep. CSS file enqueued for widgets.\n */\nclass CMB2_Widget_Boilerplate extends WP_Widget {\n\n\t/**\n\t * Unique identifier for this widget.\n\t *\n\t * Will also serve as the widget class.\n\t *\n\t * @var string\n\t */\n\tprotected $widget_slug = 'cmb2-widget-boilerplate-slug';\n\n\t/**\n\t * Shortcode name for this widget\n\t *\n\t * @var string\n\t */\n\tprotected static $shortcode = 'cmb2_widget';\n\n\t/**\n\t * This widget's CMB2 instance.\n\t *\n\t * @var CMB2\n\t */\n\tprotected $cmb2 = null;\n\n\t/**\n\t * Array of default values for widget settings.\n\t *\n\t * @var array\n\t */\n\tprotected static $defaults = array();\n\n\t/**\n\t * Store the instance properties as property\n\t *\n\t * @var array\n\t */\n\tprotected $_instance = array();\n\n\t/**\n\t * Array of CMB2 fields args.\n\t *\n\t * @var array\n\t */\n\tprotected $cmb2_fields = array();\n\n\t/**\n\t * Contruct widget.\n\t */\n\tpublic function __construct() {\n\n\t\tparent::__construct(\n\t\t\t$this->widget_slug,\n\t\t\tesc_html__( 'CMB2 Widget Boilerplate Title', 'your-textdomain' ),\n\t\t\tarray(\n\t\t\t\t'classname' => $this->widget_slug,\n\t\t\t\t'customize_selective_refresh' => true,\n\t\t\t\t'description' => esc_html__( 'A CMB2 widget boilerplate description.', 'your-textdomain' ),\n\t\t\t)\n\t\t);\n\n\t\tself::$defaults = array(\n\t\t\t'title' => esc_html__( 'CMB2 Widget Title', 'your-textdomain' ),\n\t\t\t'image' => '',\n\t\t\t'desc'  => '',\n\t\t\t'color' => '#bada55',\n\t\t);\n\n\t\t$this->cmb2_fields = array(\n\t\t\tarray(\n\t\t\t\t'name'   => 'Title',\n\t\t\t\t'id_key' => 'title',\n\t\t\t\t'id'     => 'title',\n\t\t\t\t'type'   => 'text',\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'name'    => 'Image',\n\t\t\t\t'desc'    => 'Upload an image or enter an URL.',\n\t\t\t\t'id_key'  => 'image',\n\t\t\t\t'id'      => 'image',\n\t\t\t\t'type'    => 'file',\n\t\t\t\t'options' => array(\n\t\t\t\t\t'url'\t=> false\n\t\t\t\t),\n\t\t\t\t'text' => array(\n\t\t\t\t\t'add_upload_file_text' => 'Upload An Image'\n\t\t\t\t),\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'name'   => 'Description',\n\t\t\t\t'id_key' => 'desc',\n\t\t\t\t'id'     => 'desc',\n\t\t\t\t'type'   => 'textarea',\n\t\t\t),\n\t\t\tarray(\n\t\t\t\t'name'   => 'Color',\n\t\t\t\t'id_key' => 'color',\n\t\t\t\t'id'     => 'color',\n\t\t\t\t'type'   => 'colorpicker',\n\t\t\t),\n\t\t);\n\n\t\tadd_action( 'save_post',    array( $this, 'flush_widget_cache' ) );\n\t\tadd_action( 'deleted_post', array( $this, 'flush_widget_cache' ) );\n\t\tadd_action( 'switch_theme', array( $this, 'flush_widget_cache' ) );\n\t\tadd_shortcode( self::$shortcode, array( __CLASS__, 'get_widget' ) );\n\t}\n\n\t/**\n\t * Delete this widget's cache.\n\t *\n\t * Note: Could also delete any transients\n\t * delete_transient( 'some-transient-generated-by-this-widget' );\n\t */\n\tpublic function flush_widget_cache() {\n\t\twp_cache_delete( $this->id, 'widget' );\n\t}\n\n\t/**\n\t * Front-end display of widget.\n\t *\n\t * @param  array  $args      The widget arguments set up when a sidebar is registered.\n\t * @param  array  $instance  The widget settings as set by user.\n\t */\n\tpublic function widget( $args, $instance ) {\n\n\t\techo self::get_widget( array(\n\t\t\t'args'     => $args,\n\t\t\t'instance' => $instance,\n\t\t\t'cache_id' => $this->id, // whatever the widget id is\n\t\t) );\n\n\t}\n\n\t/**\n\t * Return the widget/shortcode output\n\t *\n\t * @param  array  $atts Array of widget/shortcode attributes/args\n\t * @return string       Widget output\n\t */\n\tpublic static function get_widget( $atts ) {\n\t\t$widget = '';\n\n\t\t// Set up default values for attributes\n\t\t$atts = shortcode_atts(\n\t\t\tarray(\n\t\t\t\t// Ensure variables\n\t\t\t\t'instance'      => array(),\n\t\t\t\t'before_widget' => '',\n\t\t\t\t'after_widget'  => '',\n\t\t\t\t'before_title'  => '',\n\t\t\t\t'after_title'   => '',\n\t\t\t\t'cache_id'      => '',\n\t\t\t\t'flush_cache'   => isset( $_GET['delete-trans'] ), // Check for cache-buster\n\t\t\t),\n\t\t\tisset( $atts['args'] ) ? (array) $atts['args'] : array(),\n\t\t\tself::$shortcode\n\t\t);\n\n\t\t$instance = shortcode_atts(\n\t\t\tself::$defaults,\n\t\t\t! empty( $atts['instance'] ) ? (array) $atts['instance'] : array(),\n\t\t\tself::$shortcode\n\t\t);\n\n\t\t/*\n\t\t * If cache_id is not passed, we're not using the widget (but the shortcode),\n\t\t * so generate a hash cache id from the shortcode arguments\n\t\t */\n\t\tif ( empty( $atts['cache_id'] ) ) {\n\t\t\t$atts['cache_id'] = md5( serialize( $atts ) );\n\t\t}\n\n\t\t// Get from cache unless being requested not to\n\t\t$widget = ! $atts['flush_cache']\n\t\t\t? wp_cache_get( $atts['cache_id'], 'widget' )\n\t\t\t: '';\n\n\t\t// If $widget is empty, rebuild our cache\n\t\tif ( empty( $widget ) ) {\n\n\t\t\t$widget = '';\n\n\t\t\t// Before widget hook\n\t\t\t$widget .= $atts['before_widget'];\n\n\t\t\t$widget .= '<div style=\"background-color:'. esc_attr( $instance['color'] ) .'\">';\n\n\t\t\t// Title\n\t\t\t$widget .= ( $instance['title'] ) ? $atts['before_title'] . esc_html( $instance['title'] ) . $atts['after_title'] : '';\n\n\t\t\t$widget .= wpautop( wp_kses_post( $instance['desc'] ) );\n\n\t\t\t$widget .= '</div>';\n\n\t\t\t// After widget hook\n\t\t\t$widget .= $atts['after_widget'];\n\n\t\t\twp_cache_set( $atts['cache_id'], $widget, 'widget', WEEK_IN_SECONDS );\n\n\t\t}\n\n\t\treturn $widget;\n\t}\n\n\t/**\n\t * Update form values as they are saved.\n\t *\n\t * @param  array  $new_instance  New settings for this instance as input by the user.\n\t * @param  array  $old_instance  Old settings for this instance.\n\t * @return array  Settings to save or bool false to cancel saving.\n\t */\n\tpublic function update( $new_instance, $old_instance ) {\n\t\t$this->flush_widget_cache();\n\t\t$sanitized = $this->cmb2( true )->get_sanitized_values( $new_instance );\n\t\treturn $sanitized;\n\t}\n\t/**\n\t * Back-end widget form with defaults.\n\t *\n\t * @param  array  $instance  Current settings.\n\t */\n\tpublic function form( $instance ) {\n\t\t// If there are no settings, set up defaults\n\t\t$this->_instance = wp_parse_args( (array) $instance, self::$defaults );\n\n\t\t$cmb2 = $this->cmb2();\n\n\t\t$cmb2->object_id( $this->option_name );\n\t\tCMB2_hookup::enqueue_cmb_css();\n\t\tCMB2_hookup::enqueue_cmb_js();\n\t\t$cmb2->show_form();\n\t}\n\n\t/**\n\t * Creates a new instance of CMB2 and adds some fields\n\t * @since  0.1.0\n\t * @return CMB2\n\t */\n\tpublic function cmb2( $saving = false ) {\n\n\t\t// Create a new box in the class\n\t\t$cmb2 = new CMB2( array(\n\t\t\t'id'      => $this->option_name .'_box', // Option name is taken from the WP_Widget class.\n\t\t\t'hookup'  => false,\n\t\t\t'show_on' => array(\n\t\t\t\t'key'   => 'options-page', // Tells CMB2 to handle this as an option\n\t\t\t\t'value' => array( $this->option_name )\n\t\t\t),\n\t\t), $this->option_name );\n\n\t\tforeach ( $this->cmb2_fields as $field ) {\n\n\t\t\tif ( ! $saving ) {\n\t\t\t\t$field['id'] = $this->get_field_name( $field['id'] );\n\t\t\t}\n\n\t\t\t$field['default_cb'] = array( $this, 'default_cb' );\n\n\t\t\t$cmb2->add_field( $field );\n\t\t}\n\n\t\treturn $cmb2;\n\t}\n\n\t/**\n\t * Sets the field default, or the field value.\n\t *\n\t * @param  array      $field_args CMB2 field args array\n\t * @param  CMB2_Field $field CMB2 Field object.\n\t *\n\t * @return mixed      Field value.\n\t */\n\tpublic function default_cb( $field_args, $field ) {\n\t\treturn isset( $this->_instance[ $field->args( 'id_key' ) ] )\n\t\t\t? $this->_instance[ $field->args( 'id_key' ) ]\n\t\t\t: null;\n\t}\n\n}\n\n/**\n * Register this widget with WordPress.\n */\nfunction register_wds_widget_boilerplate() {\n\tregister_widget( 'CMB2_Widget_Boilerplate' );\n}\nadd_action( 'widgets_init', 'register_wds_widget_boilerplate' );\n"
  }
]