├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── api ├── README.md └── cmb2-file-list-ordered.php ├── conditional-display ├── exclude-for-ids.php ├── hide-on-new-post-page.php ├── show-for-taxonomy-terms.php ├── show-if-matching-meta-value.php └── show-only-for-top-level-posts.php ├── custom-field-types ├── README.md ├── address-field-type │ ├── address-field-type.php │ └── class-cmb2-render-address-field.php ├── associate-wp-menu-field.php ├── autocomplete-field-type.php ├── button-field-type.php ├── dashicon-radio-field │ └── dashicon-radio-field.php ├── default-category-field.php ├── default-tags-field.php ├── form-field-field-type.php ├── multicheck_posttype-field_type.php ├── multicheck_title-field_type.php ├── post-list-field-type.php ├── select-multiple-field-type.php ├── star-rating-field-type │ ├── css │ │ └── star-rating-field-type.css │ └── star-rating-field-type.php ├── taxonomy-radio-with-image-field-type │ ├── DJ_Taxonomy_Radio_Hierarchical_With_Image.php │ ├── DJ_Taxonomy_Radio_With_Image_Display.php │ └── taxonomy-radio-with-image-field-type.php ├── textarea-with-checkbox.php └── year-range-field-type.php ├── filters-and-actions ├── README.md ├── cmb2-add-fields-dynamically.php ├── cmb2_all_or_nothing_types-filter.php ├── cmb2_before_form-and-cmb2_after_form-hooks.php ├── cmb2_init_$cmb_id-add-fields.php ├── cmb2_init_$cmb_id-modify-object-types.php ├── cmb2_init_$cmb_id-remove-field.php ├── cmb2_init_$cmb_id-replace-field.php ├── cmb2_init_before_hookup-add-fields.php ├── cmb2_init_before_hookup-remove-cmb2-metabox.php ├── cmb2_init_before_hookup-update-existing-fields.php ├── cmb2_override_{$field_id}_meta_value-filter.php ├── custom-css-for-specific-metabox.php ├── disable-styles-on-front-end-forms.php ├── localize-date-format.php ├── override-cmb2-data-source.php └── save-default-group-field-value-based-on-other-field.php ├── front-end ├── README.md ├── cmb2-front-end-editor.php ├── cmb2-front-end-submit.php ├── cmb2-front-end-wordpress-media-uploader.php ├── cmb2-metabox-shortcode.php └── output-file-list.php ├── helper-functions ├── README.md ├── helper-functions.php ├── modify-cmb2_metabox_form-format.php ├── modify-cmb2_metabox_form-output.php └── modify-cmb2_metabox_form-save-button-text.php ├── javascript ├── README.md ├── cmb2-auto-scroll-to-new-group.php ├── cmb2-js-validation-required.php ├── dynamically-change-group-field-title-from-subfield.php ├── limit-number-of-multiple-repeat-groups.php ├── limit-number-of-repeat-fields.php └── limit-number-of-repeat-groups.php ├── metaboxes └── README.md ├── misc ├── README.md ├── add-wrap-to-group-of-fields.php ├── adding-wordcount-to-cmb2-wysiwyg-field.php ├── cmb2-field-in-publish-box.php ├── helper-functions.php ├── outputting-cmb2-fields-in-featured-image-metabox.php ├── outputting-forms-outside-metaboxes.php ├── replace-wp-excerpt-with-cmb2-field.php └── replace-wp-title-content-thumbnail-with-cmb2-fields.php ├── modified-field-types ├── README.md ├── modify-button-text-for-file-type.php └── readonly-field-type.php ├── options-and-settings-pages ├── README.md ├── add-cmb2-settings-to-other-settings-pages.php ├── custom-display-callback.php ├── genesis-cpt-archive-metabox.php ├── genesis-settings-metabox.php ├── network-options-cmb.php ├── non-cmb2-options-page.php ├── options-pages-with-submenus.php ├── options-pages-with-tabs-and-submenus.php ├── submenu-options-pages.php └── theme-options-cmb.php ├── user-meta-and-settings └── README.md └── widgets └── widget-example.php /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx 3 | 4 | ### OSX ### 5 | *.DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | # Thumbnails 12 | ._* 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | # Directories potentially created on remote AFP share 22 | .AppleDB 23 | .AppleDesktop 24 | Network Trash Folder 25 | Temporary Items 26 | .apdisk 27 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute To CMB2 Code Snippet Library 2 | 3 | The 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. 4 | 5 | ## Submitting a Snippet 6 | 7 | To 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/). 8 | 9 | Guidelines for submission: 10 | 11 | - Each snippet should be placed in it's own file. 12 | - Each file should be named appropriately based on the purpose of the snippet. 13 | - Files should be placed in the appropriate category (folder). If no category exists, you may create one with your pull request. 14 | - 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}`. 15 | - (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. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CMB2 Code Snippet Library 2 | ======================== 3 | 4 | This is a community-maintained repository of code snippets that help modify the default behavior of [CMB2](https://github.com/WebDevStudios/CMB2/). 5 | 6 | Snippets are organized into categories (folders) and each snippet is placed in its own file with a name that describes what it does. 7 | 8 | Thank you to the EDD team for [the inspiration](https://github.com/easydigitaldownloads/library) for this library. 9 | 10 | ## Submitting Your Snippet 11 | 12 | We 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/). 13 | 14 | Please refer to the [Contributing guidelines](https://github.com/WebDevStudios/CMB2-Snippet-Library/blob/master/CONTRIBUTING.md) before submitting your pull request. 15 | 16 | ## Proposing a Snippet 17 | 18 | If 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. 19 | 20 | ## Notes 21 | 22 | This 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. 23 | 24 | These 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)! -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | CMB2 API Snippets 2 | ========== 3 | 4 | Snippets related to the CMB2 API endpoints/functionality. 5 | -------------------------------------------------------------------------------- /api/cmb2-file-list-ordered.php: -------------------------------------------------------------------------------- 1 | $file_url ) { 22 | $value[] = array( 23 | 'id' => $file_id, 24 | 'url' => $file_url, 25 | ); 26 | } 27 | } 28 | 29 | return $value; 30 | } 31 | 32 | /** 33 | * Filters the value before it is sent to the REST request. 34 | * 35 | * "_yourprefix_demo_file_list" is a dynamic portion of the hook name, referring to the field id. 36 | */ 37 | add_filter( 'cmb2_get_rest_value_for__yourprefix_demo_file_list', 'yourprefix_cmb2_ordered_file_list_array_in_api', 10, 2 ); 38 | 39 | return; 40 | 41 | // Another method to make this modification to several different file_list fields. 42 | // Do not use both of these methods. Pick one. 43 | 44 | define( 'YOURPREFIX_FILE_LIST_IDS', array( 45 | '_yourprefix_demo_file_list', 46 | 'xyz' 47 | // etc. 48 | ) ); 49 | 50 | function yourprefix_cmb2_ordered_file_list_array_in_api_2( $value, $field ) { 51 | // Replace 52 | if ( in_array( $field->_id(), YOURPREFIX_FILE_LIST_IDS, true ) && ! empty( $value ) && is_array( $value ) ) { 53 | $files = $value; 54 | $value = array(); 55 | foreach ( $files as $file_id => $file_url ) { 56 | $value[] = array( 57 | 'id' => $file_id, 58 | 'url' => $file_url, 59 | ); 60 | } 61 | } 62 | 63 | return $value; 64 | } 65 | add_filter( 'cmb2_get_rest_value_file_list', 'yourprefix_cmb2_ordered_file_list_array_in_api_2', 10, 2 ); -------------------------------------------------------------------------------- /conditional-display/exclude-for-ids.php: -------------------------------------------------------------------------------- 1 | 'exclude_for_ids', 9 | 'title' => 'Demo', 10 | 'exclude_ids' => array( 1, 2, 3, 55 ), // Exclude metabox on these post-ids 11 | 'show_on_cb' => 'cmb2_exclude_for_ids', // function should return a bool value 12 | ) ); 13 | 14 | /** 15 | * Exclude metabox on specific IDs 16 | * @param object $cmb CMB2 object 17 | * @return bool True/false whether to show the metabox 18 | */ 19 | function cmb2_exclude_for_ids( $cmb ) { 20 | $ids_to_exclude = $cmb->prop( 'exclude_ids', array() ); 21 | $excluded = in_array( $cmb->object_id(), $ids_to_exclude, true ); 22 | 23 | return ! $excluded; 24 | } 25 | -------------------------------------------------------------------------------- /conditional-display/hide-on-new-post-page.php: -------------------------------------------------------------------------------- 1 | 'exclude_for_ids', 10 | 'title' => 'Demo', 11 | 'exclude_from' => array( 'post-new.php' ), // Exclude metabox on new-post screen 12 | 'show_on_cb' => 'tgm_exclude_from_new', // function should return a bool value 13 | ) ); 14 | 15 | /** 16 | * Removes metabox from appearing on post new screens before the post 17 | * ID has been set. 18 | * @author Thomas Griffin 19 | * @param object $cmb CMB2 object 20 | * @return bool True/false whether to show the metabox 21 | */ 22 | function tgm_exclude_from_new( $cmb ) { 23 | global $pagenow; 24 | 25 | $exclude_from = $cmb->prop( 'exclude_from', array( 'post-new.php' ) ); 26 | $excluded = in_array( $pagenow, $exclude_from, true ); 27 | 28 | return ! $excluded; 29 | } 30 | -------------------------------------------------------------------------------- /conditional-display/show-for-taxonomy-terms.php: -------------------------------------------------------------------------------- 1 | 'show_for_taxonomy_terms', 10 | 'title' => 'Demo', 11 | 'show_on_cb' => 'be_taxonomy_show_on_filter', // function should return a bool value 12 | 'show_on_terms' => array( 13 | 'category' => array( 'featured' ), 14 | 'post_tag' => array( 'best-of' ), 15 | ), 16 | ) ); 17 | 18 | /** 19 | * Taxonomy show_on filter 20 | * @author Bill Erickson 21 | * @param object $cmb CMB2 object 22 | * @return bool True/false whether to show the metabox 23 | */ 24 | function be_taxonomy_show_on_filter( $cmb ) { 25 | $tax_terms_to_show_on = $cmb->prop( 'show_on_terms', array() ); 26 | if ( empty( $tax_terms_to_show_on ) || ! $cmb->object_id() ) { 27 | return false; 28 | } 29 | 30 | $post_id = $cmb->object_id(); 31 | $post = get_post( $post_id ); 32 | 33 | foreach( (array) $tax_terms_to_show_on as $taxonomy => $slugs ) { 34 | if ( ! is_array( $slugs ) ) { 35 | $slugs = array( $slugs ); 36 | } 37 | 38 | $terms = $post 39 | ? get_the_terms( $post, $taxonomy ) 40 | : wp_get_object_terms( $post_id, $taxonomy ); 41 | 42 | if ( ! empty( $terms ) ) { 43 | foreach( $terms as $term ) { 44 | if ( in_array( $term->slug, $slugs, true ) ) { 45 | wp_die( ': '. print_r( 'show it', true ) .'' ); 46 | // Ok, show this metabox 47 | return true; 48 | } 49 | } 50 | } 51 | } 52 | 53 | return false; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /conditional-display/show-if-matching-meta-value.php: -------------------------------------------------------------------------------- 1 | 'wiki_status_metabox', 17 | 'title' => 'Status Metabox', 18 | 'object_types' => array( 'page', ), // Post type 19 | ) ); 20 | 21 | $cmb->add_field( array( 22 | 'name' => 'Status', 23 | 'id' => 'wiki_status', 24 | 'type' => 'select', 25 | 'default' => 'internal', 26 | 'options' => array( 27 | 'internal' => 'Internal', 28 | 'external' => 'External', 29 | ), 30 | ) ); 31 | 32 | /** 33 | * Metabox to conditionally display if the 'status' is set to 'External'. 34 | */ 35 | $cmb = new_cmb2_box( array( 36 | 'id' => 'wiki_conditonal_metabox', 37 | 'title' => 'Contact Info', 38 | 'object_types' => array( 'page', ), // Post type 39 | 'show_on_cb' => 'cmb_only_show_for_external', // function should return a bool value 40 | ) ); 41 | 42 | $cmb->add_field( array( 43 | 'name' => 'Email', 44 | 'id' => 'wiki_email', 45 | 'type' => 'text_email', 46 | ) ); 47 | } 48 | 49 | /** 50 | * Only display a metabox if the page's 'status' is 'external' 51 | * @param object $cmb CMB2 object 52 | * @return bool True/false whether to show the metabox 53 | */ 54 | function cmb_only_show_for_external( $cmb ) { 55 | $status = get_post_meta( $cmb->object_id(), 'wiki_status', 1 ); 56 | 57 | // Only show if status is 'external' 58 | return 'external' === $status; 59 | } 60 | -------------------------------------------------------------------------------- /conditional-display/show-only-for-top-level-posts.php: -------------------------------------------------------------------------------- 1 | 'exclude_for_ids', 9 | 'title' => 'Demo', 10 | 'show_on_cb' => 'ba_metabox_add_for_top_level_posts_only', // function should return a bool value 11 | ) ); 12 | 13 | /** 14 | * Exclude metabox on non top level posts 15 | * @author Travis Northcutt 16 | * @param object $cmb CMB2 object 17 | * @return bool True/false whether to show the metabox 18 | */ 19 | function ba_metabox_add_for_top_level_posts_only( $cmb ) { 20 | $has_parent = $cmb->object_id() && get_post_ancestors( $cmb->object_id() ); 21 | 22 | return ! $has_parent; 23 | } 24 | -------------------------------------------------------------------------------- /custom-field-types/README.md: -------------------------------------------------------------------------------- 1 | Custom Field Types 2 | ========== 3 | 4 | Snippets for [adding custom CMB2 field types](https://github.com/WebDevStudios/CMB2/wiki/Adding-your-own-field-types). -------------------------------------------------------------------------------- /custom-field-types/address-field-type/address-field-type.php: -------------------------------------------------------------------------------- 1 | '', 37 | 'address-2' => '', 38 | 'city' => '', 39 | 'state' => '', 40 | 'zip' => '', 41 | 'country' => '', 42 | ) ); 43 | 44 | $output = '
'; 45 | $output .= '

Address: ' . esc_html( $address['address-1'] ) . '

'; 46 | if ( $address['address-2'] ) { 47 | $output .= '

' . esc_html( $address['address-2'] ) . '

'; 48 | } 49 | $output .= '

City: ' . esc_html( $address['city'] ) . '

'; 50 | $output .= '

State: ' . esc_html( $address['state'] ) . '

'; 51 | $output .= '

Zip: ' . esc_html( $address['zip'] ) . '

'; 52 | $output .= '
'; 53 | 54 | return apply_filters( 'jt_cmb2_get_address_field', $output ); 55 | } 56 | 57 | function cmb2_init_address_field() { 58 | require_once dirname( __FILE__ ) . '/class-cmb2-render-address-field.php'; 59 | CMB2_Render_Address_Field::init(); 60 | } 61 | add_action( 'cmb2_init', 'cmb2_init_address_field' ); 62 | -------------------------------------------------------------------------------- /custom-field-types/address-field-type/class-cmb2-render-address-field.php: -------------------------------------------------------------------------------- 1 | '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' ); 14 | 15 | public static function init() { 16 | add_filter( 'cmb2_render_class_address', array( __CLASS__, 'class_name' ) ); 17 | add_filter( 'cmb2_sanitize_address', array( __CLASS__, 'maybe_save_split_values' ), 12, 4 ); 18 | 19 | /** 20 | * The following snippets are required for allowing the address field 21 | * to work as a repeatable field, or in a repeatable group. 22 | */ 23 | add_filter( 'cmb2_sanitize_address', array( __CLASS__, 'sanitize' ), 10, 5 ); 24 | add_filter( 'cmb2_types_esc_address', array( __CLASS__, 'escape' ), 10, 4 ); 25 | add_filter( 'cmb2_override_meta_value', array( __CLASS__, 'get_split_meta_value' ), 12, 4 ); 26 | } 27 | 28 | public static function class_name() { return __CLASS__; } 29 | 30 | /** 31 | * Handles outputting the address field. 32 | */ 33 | public function render() { 34 | 35 | // make sure we assign each part of the value we need. 36 | $value = wp_parse_args( $this->field->escaped_value(), array( 37 | 'address-1' => '', 38 | 'address-2' => '', 39 | 'city' => '', 40 | 'state' => '', 41 | 'zip' => '', 42 | 'country' => '', 43 | ) ); 44 | 45 | if ( ! $this->field->args( 'do_country' ) ) { 46 | $state_list = $this->field->args( 'state_list', array() ); 47 | if ( empty( $state_list ) ) { 48 | $state_list = self::$state_list; 49 | } 50 | 51 | // Add the "label" option. Can override via the field text param. 52 | $state_list = array( '' => esc_html( $this->_text( 'address_select_state_text', 'Select a State' ) ) ) + $state_list; 53 | 54 | $state_options = ''; 55 | foreach ( $state_list as $abrev => $state ) { 56 | $state_options .= ''; 57 | } 58 | } 59 | 60 | $state_label = 'State'; 61 | if ( $this->field->args( 'do_country' ) ) { 62 | $state_label .= '/Province'; 63 | } 64 | 65 | ob_start(); 66 | // Do html. 67 | ?> 68 |

69 | types->input( array( 70 | 'name' => $this->_name( '[address-1]' ), 71 | 'id' => $this->_id( '_address_1' ), 72 | 'value' => $value['address-1'], 73 | 'desc' => '', 74 | ) ); ?> 75 |
76 |

77 | types->input( array( 78 | 'name' => $this->_name( '[address-2]' ), 79 | 'id' => $this->_id( '_address_2' ), 80 | 'value' => $value['address-2'], 81 | 'desc' => '', 82 | ) ); ?> 83 |
84 |
85 |

86 | types->input( array( 87 | 'class' => 'cmb_text_small', 88 | 'name' => $this->_name( '[city]' ), 89 | 'id' => $this->_id( '_city' ), 90 | 'value' => $value['city'], 91 | 'desc' => '', 92 | ) ); ?> 93 |
94 |

95 | field->args( 'do_country' ) ) : ?> 96 | types->input( array( 97 | 'class' => 'cmb_text_small', 98 | 'name' => $this->_name( '[state]' ), 99 | 'id' => $this->_id( '_state' ), 100 | 'value' => $value['state'], 101 | 'desc' => '', 102 | ) ); ?> 103 | 104 | types->select( array( 105 | 'name' => $this->_name( '[state]' ), 106 | 'id' => $this->_id( '_state' ), 107 | 'options' => $state_options, 108 | 'desc' => '', 109 | ) ); ?> 110 | 111 |
112 |

113 | types->input( array( 114 | 'class' => 'cmb_text_small', 115 | 'name' => $this->_name( '[zip]' ), 116 | 'id' => $this->_id( '_zip' ), 117 | 'value' => $value['zip'], 118 | 'type' => 'number', 119 | 'desc' => '', 120 | ) ); ?> 121 |
122 |
123 | field->args( 'do_country' ) ) : ?> 124 |

125 | types->input( array( 126 | 'name' => $this->_name( '[country]' ), 127 | 'id' => $this->_id( '_country' ), 128 | 'value' => $value['country'], 129 | 'desc' => '', 130 | ) ); ?> 131 |
132 | 133 |

134 | _desc();?> 135 |

136 | rendered( ob_get_clean() ); 140 | } 141 | 142 | /** 143 | * Optionally save the Address values into separate fields 144 | */ 145 | public static function maybe_save_split_values( $override_value, $value, $object_id, $field_args ) { 146 | if ( ! isset( $field_args['split_values'] ) || ! $field_args['split_values'] ) { 147 | // Don't do the override. 148 | return $override_value; 149 | } 150 | 151 | $address_keys = array( 'address-1', 'address-2', 'city', 'state', 'zip' ); 152 | 153 | foreach ( $address_keys as $key ) { 154 | if ( ! empty( $value[ $key ] ) ) { 155 | update_post_meta( $object_id, $field_args['id'] . 'addr_' . $key, sanitize_text_field( $value[ $key ] ) ); 156 | } 157 | } 158 | 159 | remove_filter( 'cmb2_sanitize_address', array( __CLASS__, 'sanitize' ), 10, 5 ); 160 | 161 | // Tell CMB2 we already did the update. 162 | return true; 163 | } 164 | 165 | public static function sanitize( $check, $meta_value, $object_id, $field_args, $sanitize_object ) { 166 | 167 | // if not repeatable, bail out. 168 | if ( ! is_array( $meta_value ) || ! $field_args['repeatable'] ) { 169 | return $check; 170 | } 171 | 172 | foreach ( $meta_value as $key => $val ) { 173 | $meta_value[ $key ] = array_filter( array_map( 'sanitize_text_field', $val ) ); 174 | } 175 | 176 | return array_filter( $meta_value ); 177 | } 178 | 179 | public static function escape( $check, $meta_value, $field_args, $field_object ) { 180 | // if not repeatable, bail out. 181 | if ( ! is_array( $meta_value ) || ! $field_args['repeatable'] ) { 182 | return $check; 183 | } 184 | 185 | foreach ( $meta_value as $key => $val ) { 186 | $meta_value[ $key ] = array_filter( array_map( 'esc_attr', $val ) ); 187 | } 188 | 189 | return array_filter( $meta_value ); 190 | } 191 | 192 | public static function get_split_meta_value( $data, $object_id, $field_args, $field ) { 193 | if ( 'address' !== $field->args['type'] ) { 194 | return $data; 195 | } 196 | if ( ! isset( $field->args['split_values'] ) || ! $field->args['split_values'] ) { 197 | // Don't do the override. 198 | return $data; 199 | } 200 | 201 | $prefix = $field->args['id'] . 'addr_'; 202 | // Construct an array to iterate to fetch individual meta values for our override. 203 | // Should match the values in the render() method. 204 | $metakeys = array( 205 | 'address-1', 206 | 'address-2', 207 | 'city', 208 | 'state', 209 | 'zip', 210 | 'country', 211 | ); 212 | 213 | $newdata = array(); 214 | foreach ( $metakeys as $metakey ) { 215 | // Use our prefix to construct the whole meta key from the postmeta table. 216 | $newdata[ $metakey ] = get_post_meta( $object_id, $prefix . $metakey, true ); 217 | } 218 | 219 | return $newdata; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /custom-field-types/autocomplete-field-type.php: -------------------------------------------------------------------------------- 1 | 'autocomplete_test', 43 | 'title' => __('Autocomplete Field Examples', 'autocomplete_cmb2'), 44 | 'object_types' => array('post'), 45 | ) ); 46 | 47 | $cmb->add_field( array( 48 | 'name' => __('Related Fruit', 'autocomplete_cmb2'), 49 | 'desc' => __('Fruit that is related to this post', 'autocomplete_cmb2'), 50 | 'id' => $prefix.'related_fruit', 51 | 'type' => 'autocomplete', 52 | 'options' => array( 53 | array('value' => 1, 'name' => 'Apple'), 54 | array('value' => 2, 'name' => 'Orange'), 55 | array('value' => 3, 'name' => 'Grape') 56 | ) 57 | ) ); 58 | $cmb->add_field( array( 59 | 'name' => __('Related Fruits', 'autocomplete_cmb2'), 60 | 'desc' => __('Repeatable related fruits', 'autocomplete_cmb2'), 61 | 'id' => $prefix.'related_fruits', 62 | 'type' => 'autocomplete', 63 | 'repeatable' => true, 64 | 'repeatable_class' => 'related-fruits', 65 | 'options' => array( 66 | array('value' => 1, 'name' => 'Apple'), 67 | array('value' => 2, 'name' => 'Orange'), 68 | array('value' => 3, 'name' => 'Grape') 69 | ) 70 | ) ); 71 | $cmb->add_field( array( 72 | 'name' => __('Related Post', 'autocomplete_cmb2'), 73 | 'desc' => __('Post that is related to this one', 'autocomplete_cmb2'), 74 | 'id' => $prefix.'related_post', 75 | 'type' => 'autocomplete', 76 | 'source' => 'get_post_options', 77 | 'mapping_function' => 'autocomplete_cmb2_get_post_title_from_id' 78 | ) ); 79 | $cmb->add_field( array( 80 | 'name' => __('Related Posts', 'autocomplete_cmb2'), 81 | 'desc' => __('Posts that are related to this one', 'autocomplete_cmb2'), 82 | 'id' => $prefix.'related_posts', 83 | 'repeatable' => true, 84 | 'type' => 'autocomplete', 85 | 'source' => 'get_post_options', 86 | 'repeatable_class' => 'related-posts', 87 | 'mapping_function' => 'autocomplete_cmb2_get_post_title_from_id' 88 | ) ); 89 | } 90 | 91 | /** 92 | * Gets the post title from the ID for mapping purposes in autocompletes. 93 | * 94 | * @param int $id 95 | * @return string 96 | */ 97 | function autocomplete_cmb2_get_post_title_from_id($id) { 98 | if (empty($id)) { 99 | return ''; 100 | } 101 | 102 | $post = get_post($id); 103 | 104 | return $post->post_title; 105 | } 106 | 107 | /** 108 | * Renders the autocomplete type 109 | * 110 | * @param CMB2_Field $field_object 111 | * @param string $escaped_value The value of this field passed through the escaping filter. It defaults to sanitize_text_field. 112 | * If you need the unescaped value, you can access it via $field_type_object->value(). 113 | * @param string $object_id The id of the object you are working with. Most commonly, the post id. 114 | * @param string $object_type The type of object you are working with. Most commonly, post (this applies to all post-types), 115 | * but could also be comment, user or options-page. 116 | * @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. 117 | */ 118 | function autocomplete_cmb2_render_autocomplete($field_object, $escaped_value, $object_id, $object_type, $field_type_object) { 119 | 120 | // Store the value in a hidden field. 121 | echo $field_type_object->hidden(); 122 | 123 | if (isset($field_object->args['repeatable_class'])) { 124 | $repeatable_class = $field_object->args['repeatable_class']; 125 | } 126 | 127 | $options = $field_object->options(); 128 | 129 | // Set up the options or source PHP variables. 130 | if (empty($options)) { 131 | $source = $field_object->args['source']; 132 | $value = $field_object->args['mapping_function']($field_object->escaped_value); 133 | } else { 134 | 135 | // Set the value. 136 | if (empty($field_object->escaped_value)) { 137 | $value = ''; 138 | } else { 139 | foreach ($options as $option) { 140 | if ($option['value'] == $field_object->escaped_value) { 141 | $value = $option['name']; 142 | break; 143 | } 144 | } 145 | } 146 | } 147 | 148 | // Set up the autocomplete field. Replace the '_' with '-' to not interfere with the ID from CMB2. 149 | $id = str_replace('_', '-', $field_object->args['id']); 150 | if ( '_' !== substr( $field_object->args['id'], 0, 1 ) ) { 151 | $id = '-' . $field_object->args['id']; 152 | } 153 | 154 | // Don't use the ID on repeatable elements as it won't change; use the class instead. 155 | echo ''; 157 | 158 | if (!$field_object->args['repeatable'] && isset($field_object->args['desc'])) { 159 | echo '

'.$field_object->args['desc'].'

'; 160 | } 161 | 162 | // Now, set up the script. 163 | ?> 164 | 229 | get_results($wpdb->prepare(" 252 | SELECT ID, post_title 253 | FROM $wpdb->posts 254 | WHERE post_status = 'publish' AND post_type IN $post_type_query AND post_title LIKE %s 255 | ORDER BY post_title ASC 256 | ", $like_test), OBJECT); 257 | 258 | if ($include_empty) { 259 | $post_options = array(array('name' => '--- Select ---', 'value' => '')); 260 | } else { 261 | $post_options = array(); 262 | } 263 | 264 | foreach ($posts as $post) { 265 | $post_options[] = array( 266 | 'name' => $post->post_title, 267 | 'value' => $post->ID 268 | ); 269 | } 270 | 271 | return $post_options; 272 | } 273 | 274 | 275 | /** 276 | * Gets the jQuery autocomplete widget ready. 277 | */ 278 | function autocomplete_cmb2_admin_enqueue_scripts() { 279 | wp_enqueue_script('jquery-ui-autocomplete'); 280 | } 281 | 282 | /** 283 | * Gets the post options in JSON format for the autocomplete 284 | */ 285 | function autocomplete_cmb2_get_post_autocomplete_options() { 286 | die(json_encode(autocomplete_cmb2_get_post_options_using_post_type('post', false, '%'.$_GET['q'].'%'))); 287 | } 288 | 289 | add_action('cmb2_render_autocomplete', 'autocomplete_cmb2_render_autocomplete', 10, 5); 290 | add_action('admin_enqueue_scripts', 'autocomplete_cmb2_admin_enqueue_scripts'); 291 | add_action('wp_ajax_get_post_options', 'autocomplete_cmb2_get_post_autocomplete_options'); 292 | add_filter('cmb2_admin_init', 'autocomplete_cmb2_meta_boxes'); 293 | -------------------------------------------------------------------------------- /custom-field-types/button-field-type.php: -------------------------------------------------------------------------------- 1 | 'demo_metabox', 14 | * 'title' => __( 'Test Metabox', 'yourprefix' ), 15 | * 'object_types' => array( 'page', ), 16 | * ) ); 17 | * 18 | * $cmb_demo->add_field( array( 19 | * 'type' => 'button', 20 | * 'name' => __( 'Button', 'yourprefix' ), 21 | * 'desc' => __( 'Button description (optional)', 'yourprefix' ), 22 | * 'id' => '_yourprefix_demo_button', 23 | * 'attributes' => array( 24 | * 'value' => 'Click Me', 25 | * 'onclick' => 'alert(\'You clicked the button!\');', 26 | * ), 27 | * ) ); 28 | * } 29 | * add_action( 'cmb2_init', 'yourprefix_register_demo_metabox' ); 30 | */ 31 | 32 | // render button 33 | add_action( 'cmb2_render_button', 'jt_cmb2__cmb_render_button', 10, 5 ); 34 | function jt_cmb2__cmb_render_button( $field, $escaped_value, $object_id, $object_type, $field_type_object ) { 35 | echo $field_type_object->input( array( 36 | 'class' => 'button', 37 | 'type' => 'button', 38 | ) ); 39 | } 40 | -------------------------------------------------------------------------------- /custom-field-types/default-category-field.php: -------------------------------------------------------------------------------- 1 | args( 'taxonomy' ); 20 | $taxonomy = $taxonomy ? $taxonomy : 'category'; 21 | 22 | if ( 'post' !== $object_type ) { 23 | wp_die( 'This won\'t work for non-"post" object types!' ); 24 | } 25 | 26 | remove_meta_box( "{$object_type}div", 'post', 'side' ); 27 | 28 | post_categories_meta_box( get_post( $object_id ), array( 29 | 'args' => array( 30 | 'taxonomy' => $taxonomy, 31 | ), 32 | ) ); 33 | } 34 | add_action( 'cmb2_render_default_categories', 'cmb2_render_default_categories_field_type', 10, 4 ); 35 | 36 | function cmb2_default_categories_let_wp_save( $null, $a, $field_args, $field ) { 37 | if ( 'default_categories' === $field->args( 'type' ) ) { 38 | // Let WP handle it. 39 | return false; 40 | } 41 | 42 | return $null; 43 | } 44 | add_filter( 'cmb2_override_meta_save', 'cmb2_default_categories_let_wp_save', 10, 4 ); 45 | 46 | function cmb2_remove_default_category_metabox_for_taxonomy() { 47 | foreach ( CMB2_Boxes::get_all() as $cmb ) { 48 | foreach ( $cmb->prop( 'fields' ) as $field ) { 49 | if ( 'default_categories' === $field['type'] ) { 50 | $taxonomy = isset( $field['taxonomy'] ) ? $field['taxonomy'] : 'category'; 51 | remove_meta_box( "{$taxonomy}div", 'post', 'side' ); 52 | } 53 | } 54 | } 55 | } 56 | add_action( 'admin_init', 'cmb2_remove_default_category_metabox_for_taxonomy' ); 57 | -------------------------------------------------------------------------------- /custom-field-types/default-tags-field.php: -------------------------------------------------------------------------------- 1 | args( 'taxonomy' ); 20 | $taxonomy = $taxonomy ? $taxonomy : 'post_tag'; 21 | 22 | if ( 'post' !== $object_type ) { 23 | wp_die( 'This won\'t work for non-"post" object types!' ); 24 | } 25 | 26 | wp_enqueue_script( 'tags-box' ); 27 | add_action( 'admin_footer', 'cmb2_init_post_tag_box' ); 28 | 29 | post_tags_meta_box( get_post( $object_id ), array( 30 | 'args' => array( 31 | 'taxonomy' => $taxonomy, 32 | ), 33 | ) ); 34 | } 35 | add_action( 'cmb2_render_default_tags', 'cmb2_render_default_tags_field_type', 10, 4 ); 36 | 37 | function cmb2_init_post_tag_box() { 38 | ?> 39 | 44 | args( 'type' ) ) { 49 | // Let WP handle it. 50 | return false; 51 | } 52 | 53 | return $null; 54 | } 55 | add_filter( 'cmb2_override_meta_save', 'cmb2_default_tags_let_wp_save', 10, 4 ); 56 | 57 | function cmb2_remove_default_tag_metabox_for_taxonomy() { 58 | foreach ( CMB2_Boxes::get_all() as $cmb ) { 59 | foreach ( $cmb->prop( 'fields' ) as $field ) { 60 | if ( 'default_tags' === $field['type'] ) { 61 | $taxonomy = isset( $field['taxonomy'] ) ? $field['taxonomy'] : 'post_tag'; 62 | remove_meta_box( "tagsdiv-{$taxonomy}", 'post', 'side' ); 63 | } 64 | } 65 | } 66 | } 67 | add_action( 'admin_init', 'cmb2_remove_default_tag_metabox_for_taxonomy' ); 68 | 69 | -------------------------------------------------------------------------------- /custom-field-types/form-field-field-type.php: -------------------------------------------------------------------------------- 1 | add_field( array( 12 | * 'name' => 'Campos', 13 | * 'desc' => 'Adiciona campos ao formulário', 14 | * 'id' => '_form_fields', 15 | * 'type' => 'formfield', 16 | * 'repeatable' => true, 17 | * 'text' => array( 18 | * 'add_row_text' => 'Adicionar Campo', 19 | * 'formfield_field_id_label' => 'ID do Campo', 20 | * 'formfield_field_label_label' => 'Título do campo', 21 | * 'formfield_field_type_label' => 'Tipo do campo', 22 | * 'formfield_field_size_label' => 'Tamanho do campo', 23 | * 'formfield_text_field_option_label' => 'Texto', 24 | * 'formfield_email_field_option_label' => 'Email', 25 | * 'formfield_money_field_option_label' => 'Dinheiro', 26 | * 'formfield_date_field_option_label' => 'Data', 27 | * ), 28 | * ) ); 29 | * 30 | */ 31 | 32 | 33 | /** 34 | * Render 'formfield' custom field type 35 | * 36 | * @since 0.1.0 37 | * 38 | * @param array $field The passed in `CMB2_Field` object 39 | * @param mixed $value The value of this field escaped. 40 | * It defaults to `sanitize_text_field`. 41 | * If you need the unescaped value, you can access it 42 | * via `$field->value()` 43 | * @param int $object_id The ID of the current object 44 | * @param string $object_type The type of object you are working with. 45 | * Most commonly, `post` (this applies to all post-types), 46 | * but could also be `comment`, `user` or `options-page`. 47 | * @param object $field_type The `CMB2_Types` object 48 | */ 49 | function jt_cmb2_render_formfield_field_callback( $field, $value, $object_id, $object_type, $field_type ) { 50 | 51 | // make sure we specify each part of the value we need. 52 | $value = wp_parse_args( $value, array( 53 | 'id' => '', 54 | 'label' => '', 55 | 'type' => 'text', 56 | 'size' => '', 57 | ) ); 58 | 59 | $type_options = array( 60 | 'text' => $field_type->_text( 'formfield_text_field_option_label', 'Text' ), 61 | 'email' => $field_type->_text( 'formfield_email_field_option_label', 'Email' ), 62 | 'money' => $field_type->_text( 'formfield_money_field_option_label', 'Money' ), 63 | 'date' => $field_type->_text( 'formfield_date_field_option_label', 'Date' ), 64 | ); 65 | 66 | $types = ''; 67 | foreach ( $type_options as $type => $label ) { 68 | $selected = selected( $value['type'], $type, false ); 69 | $label = esc_html( $label ); 70 | $types .= ""; 71 | } 72 | 73 | ?> 74 | 75 | 76 | 87 | 88 | 89 | 100 | 101 | 102 | 113 | 114 | 115 | 126 | 127 |
77 | 78 | input( array( 80 | 'name' => $field_type->_name( '[id]' ), 81 | 'id' => $field_type->_id( '_id' ), 82 | 'value' => $value['id'], 83 | 'desc' => '' 84 | ) ) 85 | ?> 86 |
90 | 91 | input( array( 93 | 'name' => $field_type->_name( '[label]' ), 94 | 'id' => $field_type->_id( '_label' ), 95 | 'value' => $value['label'], 96 | 'desc' => '' 97 | ) ) 98 | ?> 99 |
103 | 104 | select( array( 106 | 'name' => $field_type->_name( '[type]' ), 107 | 'id' => $field_type->_id( '_type' ), 108 | 'options' => $types, 109 | 'desc' => '' 110 | ) ) 111 | ?> 112 |
116 | 117 | input( array( 119 | 'name' => $field_type->_name( '[size]' ), 120 | 'id' => $field_type->_id( '_size' ), 121 | 'value' => $value['size'], 122 | 'desc' => '' 123 | ) ) 124 | ?> 125 |
128 | _desc() ) : ?> 129 |

130 | _desc();?> 131 |

132 | $val ) { 148 | $val['type'] = isset( $val['type'] ) ? $val['type'] : 'text'; 149 | if ( 'text' === $val['type'] ) { 150 | unset( $val['type'] ); 151 | $val = array_filter( $val ); 152 | if ( empty( $val ) ) { 153 | unset( $meta_value[ $key ] ); 154 | continue; 155 | } else { 156 | $val['type'] = 'text'; 157 | } 158 | } 159 | $meta_value[ $key ] = array_map( 'sanitize_text_field', $val ); 160 | } 161 | 162 | return $meta_value; 163 | } 164 | add_filter( 'cmb2_sanitize_formfield', 'jt_cmb2_sanitize_formfield_field', 10, 4 ); 165 | 166 | function jt_cmb2_types_esc_formfield_field( $check, $meta_value, $field_args ) { 167 | 168 | // Nothing needed if not array value or not a repeatable field. 169 | if ( ! is_array( $meta_value ) || empty( $field_args['repeatable'] ) ) { 170 | return $check; 171 | } 172 | 173 | foreach ( $meta_value as $key => $val ) { 174 | $meta_value[ $key ] = array_map( 'esc_attr', $val ); 175 | } 176 | 177 | return $meta_value; 178 | } 179 | add_filter( 'cmb2_types_esc_formfield', 'jt_cmb2_types_esc_formfield_field', 10, 3 ); -------------------------------------------------------------------------------- /custom-field-types/multicheck_posttype-field_type.php: -------------------------------------------------------------------------------- 1 | =' ) ) { 14 | $field_type_object->type = new CMB2_Type_Radio( $field_type_object ); 15 | } 16 | 17 | $cpts = get_post_types(); 18 | // To disable the avalaibility of post types 19 | unset( $cpts[ 'nav_menu_item' ] ); 20 | unset( $cpts[ 'revision' ] ); 21 | $cpts = apply_filters( 'multicheck_posttype_' . $field->args[ '_id' ], $cpts ); 22 | $options = ''; 23 | $i = 1; 24 | $values = (array) $escaped_value; 25 | 26 | if ( $cpts ) { 27 | foreach ( $cpts as $cpt ) { 28 | $args = array( 29 | 'value' => $cpt, 30 | 'label' => $cpt, 31 | 'type' => 'checkbox', 32 | 'name' => $field->args['_name'] . '[]', 33 | ); 34 | 35 | if ( in_array( $cpt, $values ) ) { 36 | $args[ 'checked' ] = 'checked'; 37 | } 38 | $options .= $field_type_object->list_input( $args, $i ); 39 | $i++; 40 | } 41 | } 42 | 43 | $classes = false === $field->args( 'select_all_button' ) ? 'cmb2-checkbox-list no-select-all cmb2-list' : 'cmb2-checkbox-list cmb2-list'; 44 | echo $field_type_object->radio( array( 'class' => $classes, 'options' => $options ), 'multicheck_posttype' ); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /custom-field-types/multicheck_title-field_type.php: -------------------------------------------------------------------------------- 1 | add_field( array( 15 | 'name' => __( 'Fields Extra', $this->plugin_slug ), 16 | 'id' => 'extra_fields', 17 | 'type' => 'multicheck_title', 18 | 'data' => $fields 19 | ) ); 20 | */ 21 | 22 | // render Title multicheck 23 | add_action( 'cmb2_render_multicheck_title', 'cmb_render_multicheck_title', 10, 5 ); 24 | 25 | function cmb_render_multicheck_title( $field, $escaped_value, $object_id, $object_type, $field_type_object ) { 26 | $data_field = $field->args[ 'data' ]; 27 | $values = ( array ) $escaped_value; 28 | $i = 0; 29 | 30 | if ( version_compare( CMB2_VERSION, '2.2.2', '>=' ) ) { 31 | $field_type_object->type = new CMB2_Type_Multicheck( $field_type_object ); 32 | } 33 | 34 | if ( $data_field ) { 35 | foreach ( $data_field as $title => $extra_fields ) { 36 | $options = ''; 37 | foreach ( $extra_fields as $extra_field => $value ) { 38 | $args = array( 39 | 'value' => $extra_field, 40 | 'label' => $value, 41 | 'type' => 'checkbox', 42 | 'name' => $field->args[ '_name' ] . '[]', 43 | ); 44 | 45 | if ( in_array( $extra_field, $values) ) { 46 | $args[ 'checked' ] = 'checked'; 47 | } 48 | $options .= $field_type_object->list_input( $args, $i ); 49 | $i++; 50 | } 51 | echo '

'.$title.'

'; 52 | $classes = false === $field->args( 'select_all_button' ) ? 'cmb2-checkbox-list no-select-all cmb2-list' : 'cmb2-checkbox-list cmb2-list'; 53 | echo $field_type_object->radio( array( 'class' => $classes, 'options' => $options ), 'title_multicheck' ); 54 | } 55 | } else { 56 | echo __( 'Nothing' ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /custom-field-types/post-list-field-type.php: -------------------------------------------------------------------------------- 1 | args( 'select_type' ); 9 | 10 | echo $field_type->input( array( 11 | 'autocomplete' => 'off', 12 | 'style' => 'display:none' 13 | ) ); 14 | echo ''; 24 | 25 | // JS needed for modal 26 | wp_enqueue_script( 'jquery' ); 27 | wp_enqueue_script( 'jquery-ui-sortable' ); 28 | 29 | if ( !is_admin() ) { 30 | // Will need custom styling! 31 | // @todo add styles for front-end 32 | require_once( ABSPATH . 'wp-admin/includes/template.php' ); 33 | } 34 | 35 | ?> 36 | 72 | args['_name'] . '[]" id="' . $field->args['_id'] . '"'; 19 | foreach ( $field->args['attributes'] as $attribute => $value ) { 20 | $select_multiple .= " $attribute=\"$value\""; 21 | } 22 | $select_multiple .= ' />'; 23 | 24 | foreach ( $field->options() as $value => $name ) { 25 | $selected = ( $escaped_value && in_array( $value, $escaped_value ) ) ? 'selected="selected"' : ''; 26 | $select_multiple .= ''; 27 | } 28 | 29 | $select_multiple .= ''; 30 | $select_multiple .= $field_type_object->_desc( true ); 31 | 32 | echo $select_multiple; // WPCS: XSS ok. 33 | } 34 | add_action( 'cmb2_render_select_multiple', 'cmb2_render_select_multiple_field_type', 10, 5 ); 35 | 36 | 37 | /** 38 | * Sanitize the selected value. 39 | */ 40 | function cmb2_sanitize_select_multiple_callback( $override_value, $value ) { 41 | if ( is_array( $value ) ) { 42 | foreach ( $value as $key => $saved_value ) { 43 | $value[$key] = sanitize_text_field( $saved_value ); 44 | } 45 | 46 | return $value; 47 | } 48 | 49 | return; 50 | } 51 | add_filter( 'cmb2_sanitize_select_multiple', 'cmb2_sanitize_select_multiple_callback', 10, 2 ); 52 | 53 | -------------------------------------------------------------------------------- /custom-field-types/star-rating-field-type/css/star-rating-field-type.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | #cmb2-star-rating-metabox .star-cb-group { 3 | /* remove inline-block whitespace */ 4 | font-size: 0; 5 | /* flip the order so we can use the + and ~ combinators */ 6 | unicode-bidi: bidi-override; 7 | direction: rtl; 8 | /* the hidden clearer */ 9 | } 10 | #cmb2-star-rating-metabox .star-cb-group * { 11 | font-size: 1rem; 12 | } 13 | #cmb2-star-rating-metabox .star-cb-group > input { 14 | display: none; 15 | } 16 | #cmb2-star-rating-metabox .star-cb-group > input + label { 17 | /* only enough room for the star */ 18 | display: inline-block; 19 | overflow: hidden; 20 | text-indent: 9999px; 21 | width: 1.75em; 22 | white-space: nowrap; 23 | cursor: pointer; 24 | } 25 | #cmb2-star-rating-metabox .star-cb-group > input + label:before { 26 | display: inline-block; 27 | text-indent: -9999px; 28 | content: "\f154"; 29 | font-family: 'dashicons'; 30 | color: #888; 31 | font-size: 1.75em; 32 | } 33 | #cmb2-star-rating-metabox .star-cb-group > input:checked ~ label:before, 34 | #cmb2-star-rating-metabox .star-cb-group > input + label:hover ~ label:before, 35 | #cmb2-star-rating-metabox .star-cb-group > input + label:hover:before { 36 | content: "\f155"; 37 | font-family: 'dashicons'; 38 | color: #FFC926; 39 | text-shadow: 0 0 1px #333; 40 | font-size: 1.75em; 41 | } 42 | #cmb2-star-rating-metabox .star-cb-group > .star-cb-clear + label { 43 | text-indent: -9999px; 44 | width: .5em; 45 | margin-left: -.5em; 46 | } 47 | #cmb2-star-rating-metabox .star-cb-group > .star-cb-clear + label:before { 48 | width: .5em; 49 | } 50 | #cmb2-star-rating-metabox .star-cb-group:hover > input + label:before { 51 | content: "\f154"; 52 | font-family: 'dashicons'; 53 | color: #888; 54 | text-shadow: none; 55 | font-size: 1.75em; 56 | } 57 | #cmb2-star-rating-metabox .star-cb-group:hover > input + label:hover ~ label:before, 58 | #cmb2-star-rating-metabox .star-cb-group:hover > input + label:hover:before { 59 | content: "\f155"; 60 | font-family: 'dashicons'; 61 | color: #FFC926; 62 | text-shadow: 0 0 1px #333; 63 | font-size: 1.75em; 64 | } 65 | #cmb2-star-rating-metabox fieldset { 66 | border: 0; 67 | background: transparent; 68 | border-radius: 1px; 69 | } -------------------------------------------------------------------------------- /custom-field-types/star-rating-field-type/star-rating-field-type.php: -------------------------------------------------------------------------------- 1 | '; 34 | $x = 1; 35 | $total = 5; 36 | while( $x <= $rating ) { 37 | $stars_container .= ''; 38 | $x++; 39 | } 40 | if( $rating < $total ) { 41 | while( $rating < $total ) { 42 | $stars_container .= ''; 43 | $rating++; 44 | } 45 | } 46 | $stars_container .= ''; 47 | wp_enqueue_style( 'dashicons' ); 48 | return $stars_container; 49 | } 50 | 51 | /** 52 | * Render 'star rating' custom field type 53 | * 54 | * @since 0.1.0 55 | * 56 | * @param array $field The passed in `CMB2_Field` object 57 | * @param mixed $value The value of this field escaped. 58 | * It defaults to `sanitize_text_field`. 59 | * If you need the unescaped value, you can access it 60 | * via `$field->value()` 61 | * @param int $object_id The ID of the current object 62 | * @param string $object_type The type of object you are working with. 63 | * Most commonly, `post` (this applies to all post-types), 64 | * but could also be `comment`, `user` or `options-page`. 65 | * @param object $field_type_object The `CMB2_Types` object 66 | */ 67 | function eh_cmb2_render_star_rating_field_callback( $field, $value, $object_id, $object_type, $field_type_object ) { 68 | // enqueue styles 69 | wp_enqueue_style( 'star-rating-metabox-css', plugin_dir_url(__FILE__) . '/css/star-rating-field-type.css', array( 'cmb2-styles' ), 'all', false ); 70 | ?> 71 |
72 |
73 | 74 | 0 ) { 77 | ?> 78 | /> 79 | 80 | 84 | 85 |
86 |
87 | _desc( true ); 89 | 90 | } 91 | add_filter( 'cmb2_render_star_rating', 'eh_cmb2_render_star_rating_field_callback', 10, 5 ); -------------------------------------------------------------------------------- /custom-field-types/taxonomy-radio-with-image-field-type/DJ_Taxonomy_Radio_Hierarchical_With_Image.php: -------------------------------------------------------------------------------- 1 | term_id ) ) { 20 | return ''; 21 | } 22 | 23 | $this->parent = $parent_term->term_id; 24 | 25 | $terms = $this->get_terms(); 26 | $options = ''; 27 | 28 | if ( ! empty( $terms ) && is_array( $terms ) ) { 29 | // DJ - BEGIN 30 | $options = ''; 33 | // DJ - END 34 | } 35 | 36 | return $options; 37 | } 38 | 39 | protected function list_term_input( $term, $saved_term ) { 40 | $this->term = $term; 41 | $this->saved_term = $saved_term; 42 | return parent::list_term_input( $term, $saved_term ); 43 | } 44 | 45 | public function list_input( $args = array(), $i ) { 46 | if ( empty( $this->term ) ) { 47 | return parent::list_input( $args, $i ); 48 | } 49 | 50 | $a = $this->parse_args( 'list_input', array( 51 | 'type' => 'radio', 52 | 'class' => 'cmb2-option', 53 | 'name' => $this->_name(), 54 | 'id' => $this->_id( $i ), 55 | 'value' => $this->field->escaped_value(), 56 | 'label' => '', 57 | ), $args ); 58 | 59 | $taxonomy = $this->field->args( 'taxonomy' ); 60 | $image = ''; 61 | $is_parent = ''; 62 | 63 | $image_url = isset( $this->term->term_id ) ? get_term_meta( $this->term->term_id, 'yourprefix_category_avatar', true ) : ''; 64 | 65 | if ( ! empty( $image_url ) && $this->term->parent == 0 ) { 66 | $image = ''.$a['label'].''; 67 | } else { 68 | $image = ''; 69 | } 70 | 71 | $atts = $this->concat_attrs( $a, array( 'label' ) ); 72 | if ( isset( $this->term->term_id ) && get_term_children( $this->term->term_id, $taxonomy ) ) { 73 | $is_parent = 'class="parent"'; 74 | return sprintf( "\t" . '
  • ' . "\n", $is_parent, $atts, $a['id'], $image, $a['label'] ); 75 | } else { 76 | return sprintf( "\t" . '
  • ' . "\n", $is_parent, $atts, $a['id'], $image, $a['label'] ); 77 | } 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /custom-field-types/taxonomy-radio-with-image-field-type/DJ_Taxonomy_Radio_With_Image_Display.php: -------------------------------------------------------------------------------- 1 | taxonomy(); 31 | return $sanitize_object->_is_empty_array( $sanitized_value ) ? '' : $sanitized_value; 32 | } 33 | 34 | 35 | function cmb2_init_taxonomy_radio_with_image_field_type() { 36 | add_filter( 'cmb2_render_class_taxonomy_radio_with_image', 'dj_taxonomy_radio_with_image_field_type_class_name' ); 37 | add_filter( 'cmb2_display_class_taxonomy_radio_with_image', 'dj_taxonomy_radio_with_image_field_type_display_class_name' ); 38 | add_filter( 'cmb2_all_or_nothing_types', 'dj_add_taxonomy_radio_with_image' ); 39 | add_filter( 'cmb2_non_repeatable_fields', 'dj_add_taxonomy_radio_with_image' ); 40 | 41 | /** 42 | * The following snippet is required for allowing the taxonomy_radio_with_image field 43 | * to save to the term fields. 44 | */ 45 | add_filter( 'cmb2_sanitize_taxonomy_radio_with_image', 'dj_taxonomy_radio_with_image_sanitize', 10, 5 ); 46 | } 47 | add_action( 'cmb2_init', 'cmb2_init_taxonomy_radio_with_image_field_type' ); 48 | -------------------------------------------------------------------------------- /custom-field-types/textarea-with-checkbox.php: -------------------------------------------------------------------------------- 1 | '', 35 | 'status' => '', 36 | ] ); 37 | $checked = false; 38 | if ( ! empty( $field_escaped_value['status'] ) ) { 39 | $checked = true; 40 | } 41 | ?> 42 |
    43 | textarea( [ 44 | 'name' => $field_type_object->_name( '[text]' ), 45 | 'id' => $field_type_object->_id( '_text' ), 46 | 'value' => $field_escaped_value['text'], 47 | ] ); ?> 48 |
    49 |
    50 |

    51 | checkbox( [ 52 | 'type' => 'checkbox', 53 | 'name' => $field_type_object->_name( '[status]' ), 54 | 'id' => $field_type_object->_id( '_status' ), 55 | ], $checked ); ?> 56 |
    57 | _desc( true ); 59 | } 60 | 61 | /** 62 | * Sanitize Field. 63 | */ 64 | public static function sanitize_textarea_with_checkbox( $check, $meta_value, $object_id, $field_args, $sanitize_object ) { 65 | if ( !is_array( $meta_value ) || !( array_key_exists('repeatable', $field_args ) && $field_args['repeatable'] == TRUE ) ) { 66 | return $check; 67 | } 68 | 69 | $new_values = array(); 70 | foreach ( $meta_value as $key => $val ) { 71 | if( !empty( $meta_value[$key]['text'] ) ) { 72 | $new_values[$key] = array_filter( array_map( 'sanitize_text_field', $val ) ); 73 | } 74 | } 75 | 76 | return array_filter( array_values( $new_values ) ); 77 | } 78 | 79 | /** 80 | * Escape Field. 81 | */ 82 | public static function escape_textarea_with_checkbox( $check, $meta_value, $field_args, $field_object ) { 83 | if ( !is_array( $meta_value ) || ! $field_args['repeatable'] ) { 84 | return $check; 85 | } 86 | 87 | $new_values = array(); 88 | foreach ( $meta_value as $key => $val ) { 89 | if( !empty( $meta_value[$key]['text'] ) ) { 90 | $new_values[$key] = array_filter( array_map( 'esc_attr', $val ) ); 91 | } 92 | } 93 | 94 | return array_filter( array_values( $new_values ) ); 95 | } 96 | } 97 | 98 | $cmb2_field_textarea_with_checkbox = new CMB2_Field_Textarea_With_Checkbox(); 99 | } 100 | -------------------------------------------------------------------------------- /custom-field-types/year-range-field-type.php: -------------------------------------------------------------------------------- 1 | 'demo_metabox', 14 | * 'title' => __( 'Test Metabox', 'cmb2' ), 15 | * 'object_types' => array( 'page', ), 16 | * ) ); 17 | * 18 | * $cmb_demo->add_field( array( 19 | * 'name' => __( 'Date Year Range', 'cmb2' ), 20 | * 'desc' => __( 'field description (optional)', 'cmb2' ), 21 | * 'id' => 'yourprefix_demo_date_year_range', 22 | * 'type' => 'date_year_range', 23 | * // Optionally set default values. 24 | * 'options' => array( 25 | * 'earliest' => 1930, // Set the earliest year that should be shown. 26 | * // 'start_reverse_sort' => true, 27 | * // 'finish_reverse_sort' => false, 28 | * ), 29 | * 'default' => array( 30 | * 'start' => 1930, 31 | * 'finish' => 'current', 32 | * ), 33 | * // 'text' => array( 34 | * // 'start_label' => 'Start', // Optionally change start text. 35 | * // 'finish_label' => 'Finish', // Optionally change finish text. 36 | * // 'separator' => ' to ', // Optionally change separator string/text. 37 | * // ), 38 | * // 'split_values' => true, // Split values to sep. meta fields. 39 | * ) ); 40 | * 41 | * } 42 | * add_action( 'cmb2_init', 'yourprefix_register_demo_metabox' ); 43 | */ 44 | 45 | 46 | /** 47 | * Render 'date_year_range' custom field type 48 | * 49 | * @since 0.1.0 50 | * 51 | * @param array $field The passed in `CMB2_Field` object 52 | * @param mixed $value The value of this field escaped. 53 | * It defaults to `sanitize_text_field`. 54 | * If you need the unescaped value, you can access it 55 | * via `$field->value()` 56 | * @param int $object_id The ID of the current object 57 | * @param string $object_type The type of object you are working with. 58 | * Most commonly, `post` (this applies to all post-types), 59 | * but could also be `comment`, `user` or `options-page`. 60 | * @param object $type_object The `CMB2_Types` object 61 | */ 62 | function jt_cmb2_date_year_range( $field, $value, $object_id, $object_type, $type_object ) { 63 | $earliest = $field->options( 'earliest' ); 64 | $earliest = $earliest ? absint( $earliest ) : 1900; 65 | 66 | $start_reverse_sort = $field->options( 'start_reverse_sort' ); 67 | $start_reverse_sort = $start_reverse_sort ? true : false; 68 | 69 | $finish_reverse_sort = $field->options( 'finish_reverse_sort' ); 70 | $finish_reverse_sort = $finish_reverse_sort ? true : false; 71 | 72 | $value = wp_parse_args( $value, array( 73 | 'start' => '', 74 | 'finish' => '', 75 | ) ); 76 | 77 | $desc = $field->args( 'description' ); 78 | $field->args['description'] = ''; 79 | $type_object->type = new CMB2_Type_Select( $type_object ); 80 | 81 | echo ''. $type_object->_text( 'start_label', 'Starting Year' ) . ' '; 82 | 83 | $start_options = jt_cmb2_date_year_range_options( $type_object, $earliest, $value['start'], $start_reverse_sort ); 84 | echo $type_object->select( array( 85 | 'name' => $type_object->_name( '[start]' ), 86 | 'id' => $type_object->_id( '_start' ), 87 | 'value' => $value['start'], 88 | 'class' => 'cmb2_select cmb2-year-range-start', 89 | 'options' => $start_options, 90 | 'desc' => '', 91 | ) ); 92 | 93 | echo $type_object->_text( 'separator', ' — ' ); 94 | 95 | $end_options = jt_cmb2_date_year_range_options( $type_object, $earliest, $value['finish'], $finish_reverse_sort ); 96 | echo $type_object->select( array( 97 | 'name' => $type_object->_name( '[finish]' ), 98 | 'id' => $type_object->_id( '_finish' ), 99 | 'value' => $value['finish'], 100 | 'class' => 'cmb2_select cmb2-year-range-end', 101 | 'options' => $end_options, 102 | 'desc' => '', 103 | ) ); 104 | echo ' '. $type_object->_text( 'finish_label', 'Final Year' ) . ''; 105 | 106 | $field->args['description'] = $desc; 107 | 108 | $type_object->_desc( true, true ); 109 | 110 | add_action( is_admin() ? 'admin_footer' : 'wp_footer', 'jt_cmb2_date_year_range_js' ); 111 | 112 | } 113 | add_filter( 'cmb2_render_date_year_range', 'jt_cmb2_date_year_range', 10, 5 ); 114 | 115 | function jt_cmb2_date_year_range_js() { 116 | static $done = false; 117 | if ( ! $done ) { 118 | $done = true; 119 | } 120 | ?> 121 | 170 | '', 178 | 'label' => __( 'Not Set' ), 179 | ); 180 | 181 | if ( cmb2_utils()->isempty( $value ) ) { 182 | $not_set['checked'] = 'checked'; 183 | } 184 | 185 | for ( $i = $earliest; $i <= date( 'Y' ); $i++ ) { 186 | 187 | $a = array( 'value' => $i, 'label' => $i ); 188 | if ( absint( $value ) === $i ) { 189 | $a['checked'] = 'checked'; 190 | } 191 | 192 | $options[] = $a; 193 | } 194 | 195 | $a = array( 196 | 'value' => 'current', 197 | 'label' => __( 'Current' ), 198 | ); 199 | 200 | if ( 'current' === $value ) { 201 | $a['checked'] = 'checked'; 202 | } 203 | 204 | $options[] = $a; 205 | 206 | if ( $reverse ) { 207 | $options = array_reverse( $options ); 208 | } 209 | 210 | array_unshift( $options, $not_set ); 211 | 212 | return implode( "\n", array_map( array( $type_object, 'select_option' ), $options ) ); 213 | } 214 | 215 | /** 216 | * Optionally save the values into separate fields. 217 | */ 218 | function jt_cmb2_date_year_range_split_values( $override_value, $value, $object_id, $field_args ) { 219 | if ( ! isset( $field_args['split_values'] ) || ! $field_args['split_values'] ) { 220 | // Don't do the override 221 | return $override_value; 222 | } 223 | 224 | $keys = array( 'start', 'finish' ); 225 | 226 | foreach ( $keys as $key ) { 227 | if ( ! empty( $value[ $key ] ) ) { 228 | update_post_meta( $object_id, $field_args['id'] . '_'. $key, $value[ $key ] ); 229 | } 230 | } 231 | 232 | // Tell CMB2 we already did the update 233 | return true; 234 | } 235 | add_filter( 'cmb2_sanitize_date_year_range', 'jt_cmb2_date_year_range_split_values', 12, 4 ); 236 | 237 | /** 238 | * Optionally fetch the values from separate fields as well. 239 | */ 240 | function jt_cmb2_date_year_range_get_split_values( $no_override, $object_id, $args, $field ) { 241 | if ( 'date_year_range' !== $field->args( 'type' ) || ! $field->args( 'split_values' ) ) { 242 | return $no_override; 243 | } 244 | 245 | $value = array( 246 | 'start' => get_post_meta( $object_id, $args['field_id'] . '_start', 1 ), 247 | 'finish' => get_post_meta( $object_id, $args['field_id'] . '_finish', 1 ), 248 | ); 249 | 250 | return $value; 251 | } 252 | add_filter( 'cmb2_override_meta_value', 'jt_cmb2_date_year_range_get_split_values', 10, 4 ); 253 | 254 | /** 255 | * The following snippets are required for allowing the date_year_range field 256 | * to work as a repeatable field, or in a repeatable group 257 | */ 258 | 259 | function jt_cmb2_sanitize_date_year_range( $check, $meta_value, $object_id, $field_args, $sanitizer ) { 260 | 261 | // if not repeatable, bail out. 262 | if ( ! is_array( $meta_value ) || ! $field_args['repeatable'] ) { 263 | return $check; 264 | } 265 | 266 | foreach ( $meta_value as $key => $val ) { 267 | $meta_value[ $key ] = array_filter( array_map( 'sanitize_text_field', $val ) ); 268 | } 269 | 270 | return array_filter( $meta_value ); 271 | } 272 | add_filter( 'cmb2_sanitize_date_year_range', 'jt_cmb2_sanitize_date_year_range', 10, 5 ); 273 | 274 | function jt_cmb2_esc_date_year_range( $check, $meta_value, $field_args, $field_object ) { 275 | 276 | // if not repeatable, bail out. 277 | if ( ! is_array( $meta_value ) || ! $field_args['repeatable'] ) { 278 | return $check; 279 | } 280 | 281 | foreach ( $meta_value as $key => $val ) { 282 | $meta_value[ $key ] = array_filter( array_map( 'esc_attr', $val ) ); 283 | } 284 | 285 | return array_filter( $meta_value ); 286 | } 287 | add_filter( 'cmb2_types_esc_date_year_range', 'jt_cmb2_esc_date_year_range', 10, 4 ); 288 | -------------------------------------------------------------------------------- /filters-and-actions/README.md: -------------------------------------------------------------------------------- 1 | CMB2 Filters and Actions 2 | ========== 3 | 4 | CMB2 has many filters and actions. Included here are snippets which demonstrate some handy ways to use those hooks. -------------------------------------------------------------------------------- /filters-and-actions/cmb2-add-fields-dynamically.php: -------------------------------------------------------------------------------- 1 | 'dynamic_fields_box', 11 | 'title' => 'Some test metaboxes', 12 | 'object_types' => array( 'page', 'post' ), 13 | ) ); 14 | 15 | $cmb->add_field( array( 16 | 'name' => 'Set number of next item', 17 | 'id' => 'number_of_next_item', 18 | 'type' => 'text', 19 | 'default' => '1', 20 | 'attributes' => array( 21 | 'type' => 'number', 22 | 'pattern' => '\d*', 23 | ), 24 | 'sanitization_cb' => 'absint', 25 | 'escape_cb' => 'absint', 26 | ) ); 27 | 28 | // Add dynamic fields during normal view. 29 | add_action( 'cmb2_init_hookup_dynamic_fields_box', 'add_fields_dynamically_to_box' ); 30 | 31 | // Add dynamic fields during save process. 32 | add_action( 'cmb2_post_process_fields_dynamic_fields_box', 'add_fields_dynamically_to_box' ); 33 | } 34 | 35 | function add_fields_dynamically_to_box( $cmb ) { 36 | if ( $cmb->object_id() ) { 37 | $position = 2; 38 | 39 | // Loop through however many items are selected in previous field 40 | $number_of_items = get_post_meta( $cmb->object_id(), 'number_of_next_item', true ); 41 | $number = 1; 42 | while ( $number <= $number_of_items ) { 43 | 44 | $cmb->add_field( array( 45 | 'name' => 'Item #' . $number, 46 | 'desc' => 'item_' . $number, 47 | 'id' => 'item_' . $number, 48 | 'type' => 'text', 49 | ), $position++ ); 50 | 51 | $number++; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /filters-and-actions/cmb2_all_or_nothing_types-filter.php: -------------------------------------------------------------------------------- 1 | '; 17 | echo '

    Welcome to my metabox!

    '; 18 | 19 | add_action( 'cmb2_after_form', 'yourprefix_add_html_after_cmb2_output', 10, 4 ); 20 | } 21 | add_action( 'cmb2_before_form', 'yourprefix_add_html_before_cmb2_output', 10, 4 ); 22 | 23 | function yourprefix_add_html_after_cmb2_output( $cmb_id, $object_id, $object_type, $cmb ) { 24 | echo ''; 25 | } 26 | 27 | 28 | // This can be done a bit more simply this way: 29 | 30 | function add_html_before_yourprefix_demo_metabox_output( $cmb_id, $object_id, $object_type, $cmb ) { 31 | echo '
    '; 32 | echo '

    Welcome to my metabox!

    '; 33 | 34 | add_action( "cmb2_after_{$object_type}_form_{$cmb_id}", 'add_html_after_yourprefix_demo_metabox_output', 10, 4 ); 35 | } 36 | // Only output above the _yourprefix_demo_metabox metabox. 37 | add_action( 'cmb2_before_post_form__yourprefix_demo_metabox', 'add_html_before_yourprefix_demo_metabox_output', 10, 4 ); 38 | 39 | function add_html_after_yourprefix_demo_metabox_output( $cmb_id, $object_id, $object_type, $cmb ) { 40 | echo '
    '; 41 | } 42 | -------------------------------------------------------------------------------- /filters-and-actions/cmb2_init_$cmb_id-add-fields.php: -------------------------------------------------------------------------------- 1 | add_field( 9 | array( 10 | 'name' => __( 'New at the top', 'your_textdomain' ), 11 | 'desc' => __( 'Using the "cmb2_init_{$cmb_id}" hook', 'your_textdomain' ), 12 | 'id' => '_new_at_the_top', 13 | 'type' => 'text', 14 | ), 15 | 1 16 | ); 17 | 18 | } 19 | add_action( 'cmb2_init__yourprefix_demo_metabox', 'yourprefix_add_new_field_to_top_of_demo_metabox' ); 20 | -------------------------------------------------------------------------------- /filters-and-actions/cmb2_init_$cmb_id-modify-object-types.php: -------------------------------------------------------------------------------- 1 | box_types(); 14 | 15 | $types[] = 'books'; // Your custom post type slug. 16 | 17 | // Bam. 18 | $cmb->set_prop( 'object_types', $types ); 19 | } 20 | add_action( 'cmb2_init_yourprefix_demo_metabox', 'yourprefix_demo_metabox_modify_object_types' ); 21 | -------------------------------------------------------------------------------- /filters-and-actions/cmb2_init_$cmb_id-remove-field.php: -------------------------------------------------------------------------------- 1 | remove_field( '_yourprefix_demo_textsmall' ); 8 | } 9 | add_action( 'cmb2_init__yourprefix_demo_metabox', 'yourprefix_remove_field_from_demo_metabox' ); 10 | -------------------------------------------------------------------------------- /filters-and-actions/cmb2_init_$cmb_id-replace-field.php: -------------------------------------------------------------------------------- 1 | remove_field( '_yourprefix_demo_textsmall' ); 14 | 15 | $cmb->add_field( 16 | array( 17 | 'name' => __( 'REPLACED Text Small Field', 'cmb2' ), 18 | 'desc' => __( 'Using the "cmb2_init_{$cmb_id}" hook', 'your_textdomain' ), 19 | 'id' => '_yourprefix_demo_textsmall', 20 | 'type' => 'text_money', 21 | ), 22 | 17 /* This needs to be the nth position of the original field */ 23 | ); 24 | } 25 | 26 | } 27 | add_action( 'cmb2_init_hookup__yourprefix_demo_metaboxx', 'yourprefix_replace_field_in_demo_metaboxx', 999 ); 28 | -------------------------------------------------------------------------------- /filters-and-actions/cmb2_init_before_hookup-add-fields.php: -------------------------------------------------------------------------------- 1 | update_field_property( '_yourprefix_demo_text2', 'type', 'text' ); 15 | 16 | /** 17 | * Since '_yourprefix_demo_text2' doesn't exist, Let's create it. 18 | * Always need to compare this value strictly to false, as a field_id COULD be 0 or '' 19 | */ 20 | if ( false === $field_id ) { 21 | $cmb->add_field( 22 | // Normal field setup 23 | array( 24 | 'name' => __( 'Test Text 2', 'your_textdomain' ), 25 | 'desc' => __( 'Test Text 2 description', 'your_textdomain' ), 26 | 'id' => '_yourprefix_demo_text2', 27 | 'type' => 'text', 28 | 'attributes' => array( 'placeholder' => __( "I'm some placeholder text", 'your_textdomain' ) ), 29 | ), 30 | 3 // Insert this field in the third position 31 | ); 32 | 33 | } 34 | 35 | } 36 | add_action( 'cmb2_init_before_hookup', 'yourprefix_add_new_field_in_3rd_position' ); 37 | 38 | function yourprefix_add_new_field_to_group() { 39 | // Try to get a metabox w/ the id of '_yourprefix_group_metabox' 40 | if ( $cmb_group_demo = cmb2_get_metabox( '_yourprefix_group_metabox' ) ) { 41 | 42 | $cmb_group_demo->add_group_field( 43 | array( 44 | 'name' => __( 'Test Text 2', 'your_textdomain' ), 45 | 'desc' => __( 'field description (optional)', 'your_textdomain' ), 46 | 'id' => 'text2', 47 | 'type' => 'text', 48 | 'attributes' => array( 'placeholder' => __( "I'm some placeholder text", 'your_textdomain' ) ), 49 | ), 50 | '_yourprefix_group_demo', // Add this to the _yourprefix_group_demo group field 51 | 2 // And insert it into the 2nd position 52 | ); 53 | 54 | } 55 | } 56 | add_action( 'cmb2_init_before_hookup', 'yourprefix_add_new_field_to_group' ); 57 | -------------------------------------------------------------------------------- /filters-and-actions/cmb2_init_before_hookup-remove-cmb2-metabox.php: -------------------------------------------------------------------------------- 1 | update_field_property( '_yourprefix_demo_text', 'show_on_cb', false ); 18 | 19 | /** 20 | * Always need to compare this value strictly to false, as a field_id COULD be 0 or '' 21 | */ 22 | if ( false !== $field_id ) { 23 | 24 | /** 25 | * Because we don't want to 'stomp' a field's 'attributes' property 26 | * (It may already have some attributes), we're going to get 27 | * the field's attributes property and append to it. 28 | */ 29 | 30 | // Get all fields for this metabox 31 | $fields = $cmb->prop( 'fields' ); 32 | 33 | // Get the attributes array if it exists, or else create it 34 | $attributes = isset( $fields['_yourprefix_demo_text']['attributes'] ) 35 | ? $fields['_yourprefix_demo_text']['attributes'] 36 | : array(); 37 | 38 | // Add placeholder text 39 | $attributes['placeholder'] = __( "I'm some placeholder text", 'your_textdomain' ); 40 | 41 | // Update the field's 'attributes' property 42 | $cmb->update_field_property( '_yourprefix_demo_text', 'attributes', $attributes ); 43 | 44 | } 45 | 46 | } 47 | add_action( 'cmb2_init_before_hookup', 'yourprefix_update_fields_properties' ); 48 | -------------------------------------------------------------------------------- /filters-and-actions/cmb2_override_{$field_id}_meta_value-filter.php: -------------------------------------------------------------------------------- 1 | get( $args['field_id'] ) 43 | : get_metadata( $args['type'], $args['id'], $args['field_id'], ( $args['single'] || $args['repeat'] ) ); 44 | 45 | // Get the default values from JSON 46 | if ( null === $defaults ) { 47 | // Get your JSON blob.. hard-coded for demo. 48 | $json = '[{"description":"This is a description<\/strong>","image":"\/wp-content\/uploads\/2016\/10\/default-image-1.jpg","image_id":663},{"title":"2nd Title","description":"This is a second description<\/strong>","image":"\/wp-content\/uploads\/2016\/10\/default-image-2.jpg","image_id":655,"image_caption":"This is an image caption."}]'; 49 | 50 | $defaults = json_decode( $json, 1 ); 51 | } 52 | 53 | // Set our group field value to the default. 54 | $value = $defaults; 55 | 56 | // If the group field's retrieved value is not empty... 57 | if ( ! empty( $data ) ) { 58 | $value = array(); 59 | // Then loop the defaults and mash the field's value up w/ the default. 60 | foreach ( $defaults as $key => $default_group_val ) { 61 | $value[ $key ] = isset( $data[ $key ] ) 62 | ? wp_parse_args( $data[ $key ], $default_group_val ) 63 | : $default_group_val; 64 | } 65 | } 66 | 67 | return $value; 68 | } 69 | 70 | add_filter( 'cmb2_override_yourprefix_group_demo_meta_value', 'yourprefix_get_default_group_value_from_json', 10, 4 ); 71 | -------------------------------------------------------------------------------- /filters-and-actions/custom-css-for-specific-metabox.php: -------------------------------------------------------------------------------- 1 | 'custom_css_test', 12 | 'title' => __( 'Custom CSS Test', 'cmb2' ), 13 | 'object_types' => array( 'page', ), 14 | ) ); 15 | 16 | $cmb->add_field( array( 17 | 'id' => '_cmb2_test_text', 18 | 'type' => 'text', 19 | ) ); 20 | } 21 | add_action( 'cmb2_admin_init', 'js_custom_css_for_metabox' ); 22 | 23 | function js_add_custom_css_for_metabox( $post_id, $cmb ) { 24 | ?> 25 | 30 | 'Hey, this is your first entry', 20 | 'description' => 'Go ahead and delete this entry, or update its contents', 21 | ), 22 | // Default group 2. 23 | array( 24 | 'title' => 'Hey, this is your 2nd entry', 25 | 'description' => '#2', 26 | ), 27 | ) ); 28 | } 29 | 30 | $cmb_group = new_cmb2_box( array( 31 | 'id' => 'yourprefix_group_alt_data_metabox', 32 | 'title' => __( 'Repeating Field Group', 'cmb2' ), 33 | 'object_types' => array( 'post', ), 34 | 'show_on' => array( 'id' => array( 1000, ) ), 35 | 'show_in_rest' => WP_REST_Server::ALLMETHODS, 36 | ) ); 37 | 38 | // $group_field_id is the field id string, so in this case: '_yourprefix_group_demo' 39 | $group_field_id = $cmb_group->add_field( array( 40 | 'id' => 'yourprefix_group_alt_data_demo', 41 | 'type' => 'group', 42 | 'options' => array( 43 | 'group_title' => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number 44 | 'add_button' => __( 'Add Another Entry', 'cmb2' ), 45 | 'remove_button' => __( 'Remove Entry', 'cmb2' ), 46 | 'sortable' => true, // beta 47 | ), 48 | ) ); 49 | 50 | /** 51 | * Group fields works the same, except ids only need 52 | * to be unique to the group. Prefix is not needed. 53 | * 54 | * The parent field's id needs to be passed as the second argument. 55 | */ 56 | $cmb_group->add_group_field( $group_field_id, array( 57 | 'name' => 'Entry Title', 58 | 'id' => 'title', 59 | 'type' => 'text', 60 | ) ); 61 | 62 | $cmb_group->add_group_field( $group_field_id, array( 63 | 'name' => 'Description', 64 | 'description' => 'Write a short description for this entry', 65 | 'id' => 'description', 66 | 'type' => 'wysiwyg', 67 | 'options' => array( 68 | 'textarea_rows' => 3, 69 | ), 70 | ) ); 71 | }; 72 | 73 | add_filter( 'cmb2_override_yourprefix_group_alt_data_demo_meta_value', 'yourprefix_group_alt_data_demo_override_meta_value', 10, 4 ); 74 | function yourprefix_group_alt_data_demo_override_meta_value( $data, $object_id, $args, $field ) { 75 | 76 | // Here, we're pulling from the options table, but you can query from any data source here. 77 | // If from a custom table, you can use the $object_id to query against. 78 | return get_option( 'yourprefix_group_alt_data_demo', array() ); 79 | } 80 | 81 | add_filter( 'cmb2_override_yourprefix_group_alt_data_demo_meta_save', 'yourprefix_group_alt_data_demo_override_meta_save', 10, 4 ); 82 | function yourprefix_group_alt_data_demo_override_meta_save( $override, $args, $field_args, $field ) { 83 | 84 | // Here, we're storing the data to the options table, but you can store to any data source here. 85 | // If to a custom table, you can use the $args['id'] as the reference id. 86 | $updated = update_option( 'yourprefix_group_alt_data_demo', $args['value'] ); 87 | return !! $updated; 88 | } 89 | 90 | add_filter( 'cmb2_override_yourprefix_group_alt_data_demo_meta_remove', 'yourprefix_group_alt_data_demo_override_meta_remove', 10, 4 ); 91 | function yourprefix_group_alt_data_demo_override_meta_remove( $override, $args, $field_args, $field ) { 92 | 93 | // Here, we're removing from the options table, but you can query to remove from any data source here. 94 | // If from a custom table, you can use the $args['id'] to query against. 95 | // (If we do "delete_option", then our default value will be re-applied, which isn't desired.) 96 | $updated = update_option( 'yourprefix_group_alt_data_demo', array() ); 97 | return !! $updated; 98 | } 99 | -------------------------------------------------------------------------------- /filters-and-actions/save-default-group-field-value-based-on-other-field.php: -------------------------------------------------------------------------------- 1 | 'yourprefix_group_metabox', 14 | 'title' => __( 'Repeating Field Group', 'cmb2' ), 15 | 'object_types' => array( 'page', ), 16 | ) ); 17 | 18 | $cmb->add_field( $group_field_id, array( 19 | 'name' => __( 'Radio', 'cmb2' ), 20 | 'id' => 'yourprefix_group_radio', 21 | 'type' => 'radio', 22 | 'options' => array( 23 | 'standard' => __( 'Option One', 'cmb2' ), 24 | 'custom' => __( 'Option Two', 'cmb2' ), 25 | 'none' => __( 'Option Three', 'cmb2' ), 26 | ), 27 | ) ); 28 | 29 | $group_field_id = $cmb->add_field( array( 30 | 'id' => 'yourprefix_group_demo', 31 | 'type' => 'group', 32 | 'description' => __( 'Generates reusable form entries', 'cmb2' ), 33 | 'options' => array( 34 | 'group_title' => __( 'Entry {#}', 'cmb2' ), 35 | 'add_button' => __( 'Add Another Entry', 'cmb2' ), 36 | 'remove_button' => __( 'Remove Entry', 'cmb2' ), 37 | 'sortable' => true, 38 | ), 39 | ) ); 40 | 41 | $cmb->add_group_field( $group_field_id, array( 42 | 'name' => 'Entry Title', 43 | 'id' => 'title', 44 | 'type' => 'text', 45 | ) ); 46 | 47 | $cmb->add_group_field( $group_field_id, array( 48 | 'name' => 'Description', 49 | 'description' => 'Write a short description for this entry', 50 | 'id' => 'description', 51 | 'type' => 'textarea_small', 52 | ) ); 53 | 54 | } 55 | add_action( 'cmb2_init', 'yourprefix_register_repeatable_group_field_metabox' ); 56 | 57 | 58 | // If the radio field is set to 'standard', then update the group field value to have one group filled-in. 59 | function hook_in_and_add_default_group_value( $post_id, $updated, $cmb ) { 60 | // If 'my_meta_key' was updated, then proceed w/ my stuff. 61 | if ( in_array( 'yourprefix_group_radio', $updated ) ) { 62 | if ( 'standard' === get_post_meta( $post_id, 'yourprefix_group_radio', 1 ) ) { 63 | // do stuff 64 | update_post_meta( $post_id, 'yourprefix_group_demo', array( 65 | array( 66 | 'title' => 'Title of group 1', 67 | 'description' => 'Description of group 1', 68 | ) 69 | ) ); 70 | } 71 | } 72 | } 73 | add_action( 'cmb2_save_post_fields_yourprefix_group_metabox', 'hook_in_and_add_default_group_value', 10, 3 ); 74 | -------------------------------------------------------------------------------- /front-end/README.md: -------------------------------------------------------------------------------- 1 | Front-end 2 | ========== 3 | 4 | Snippets that use CMB2 on the front-end (not wp-admin) side of your site. -------------------------------------------------------------------------------- /front-end/cmb2-front-end-editor.php: -------------------------------------------------------------------------------- 1 | You do not have permission to edit this post.'; 12 | } 13 | } 14 | 15 | return $content; 16 | } 17 | add_filter( 'the_content', 'jt_add_edit_form_to_frontend' ); 18 | 19 | /** 20 | * Modify the edit links to point to the front-end editor. 21 | */ 22 | function jt_modify_edit_link( $link ) { 23 | if ( ! is_admin() ) { 24 | $link = esc_url_raw( wp_nonce_url( remove_query_arg( 'edit' ), 'edit', 'edit' ) ); 25 | } 26 | return $link; 27 | } 28 | add_filter( 'get_edit_post_link', 'jt_modify_edit_link' ); 29 | 30 | /** 31 | * Hook in later and prepend our title/content fields to our existing metabox 32 | */ 33 | function jt_edit_core_fields() { 34 | if ( ! is_admin() ) { // only if on front-end 35 | 36 | // Get existing metabox 37 | $cmb = cmb2_get_metabox( '_yourprefix_demo_metabox' ); 38 | 39 | // and prepend title 40 | $cmb->add_field( array( 41 | 'name' => __( 'Title', 'cmb2' ), 42 | 'id' => 'post_title', 43 | 'type' => 'text', 44 | 'before' => 'jt_edit_core_maybe_redirect', 45 | ), 1 ); 46 | 47 | // and content fields 48 | $cmb->add_field( array( 49 | 'name' => __( 'Content', 'cmb2' ), 50 | 'id' => 'post_content', 51 | 'type' => 'wysiwyg', 52 | ), 2 ); 53 | } 54 | } 55 | add_action( 'cmb2_init', 'jt_edit_core_fields', 99 ); 56 | 57 | /** 58 | * If edit was saved, redirect to non-edit page 59 | */ 60 | function jt_edit_core_maybe_redirect() { 61 | if ( isset( $_POST['post_content'] ) ) { 62 | $url = esc_url_raw( remove_query_arg( 'edit' ) ); 63 | echo ""; 64 | } 65 | } 66 | 67 | /** 68 | * We don't want CMB2 to fetch data from meta for post title and post content 69 | */ 70 | function jt_cmb2_override_core_field_get( $val, $object_id, $a, $field ) { 71 | global $post; 72 | 73 | if ( in_array( $field->id(), array( 'post_title', 'post_content' ), true ) ) { 74 | if ( isset( $post->ID ) ) { 75 | $val = get_post_field( $field->id(), $post ); 76 | } else { 77 | $val = ''; 78 | } 79 | } 80 | 81 | return $val; 82 | } 83 | add_filter( 'cmb2_override_meta_value', 'jt_cmb2_override_core_field_get', 10, 4 ); 84 | 85 | /** 86 | * We don't want CMB2 to save data to meta for post title and post content 87 | */ 88 | function jt_cmb2_override_core_field_set( $status, $a, $args, $field ) { 89 | global $post; 90 | 91 | if ( in_array( $field->id(), array( 'post_title', 'post_content' ), true ) ) { 92 | if ( isset( $post->ID ) ) { 93 | $status = wp_update_post( array( 94 | $field->id() => $a['value'], 95 | 'ID' => $post->ID, 96 | ) ); 97 | 98 | } else { 99 | $status = false; 100 | } 101 | } 102 | 103 | return $status; 104 | } 105 | add_filter( 'cmb2_override_meta_save', 'jt_cmb2_override_core_field_set', 10, 4 ); 106 | -------------------------------------------------------------------------------- /front-end/cmb2-front-end-wordpress-media-uploader.php: -------------------------------------------------------------------------------- 1 | add_cap( 'upload_files' ); 30 | 31 | } 32 | 33 | add_action( 'init', 'nevestam_allow_contributor_uploads' ); 34 | 35 | /** 36 | * Display only user-uploaded files to each user 37 | * 38 | * @param WP_Query $wp_query_obj 39 | */ 40 | function nevestam_restrict_media_library( $wp_query_obj ) { 41 | global $current_user, $pagenow; 42 | 43 | if ( ! is_a( $current_user, 'WP_User' ) ) { 44 | return; 45 | } 46 | 47 | if ( 'admin-ajax.php' != $pagenow || 'query-attachments' != $_REQUEST['action'] ) { 48 | return; 49 | } 50 | 51 | if ( ! current_user_can( 'manage_media_library' ) ) { 52 | $wp_query_obj->set( 'author', $current_user->ID ); 53 | } 54 | } 55 | 56 | add_action( 'pre_get_posts', 'nevestam_restrict_media_library' ); 57 | -------------------------------------------------------------------------------- /front-end/cmb2-metabox-shortcode.php: -------------------------------------------------------------------------------- 1 | ID; 31 | } 32 | 33 | // If no metabox id is set, yell about it 34 | if ( empty( $atts['id'] ) ) { 35 | return 'Please add an "id" attribute to specify the CMB2 form to display.'; 36 | } 37 | 38 | $metabox_id = esc_attr( $atts['id'] ); 39 | $object_id = absint( $atts['post_id'] ); 40 | // Get our form 41 | $form = cmb2_get_metabox_form( $metabox_id, $object_id ); 42 | 43 | return $form; 44 | } 45 | add_shortcode( 'cmb-form', 'jt_cmb2_do_frontend_form_shortcode' ); 46 | -------------------------------------------------------------------------------- /front-end/output-file-list.php: -------------------------------------------------------------------------------- 1 | $attachment_url ) { 34 | $images .= '
    '; 35 | $images .= wp_get_attachment_image( $attachment_id, $img_size ); 36 | $images .= '
    '; 37 | } 38 | 39 | return $images ? '
    ' . $images . '
    ' : ''; 40 | } 41 | -------------------------------------------------------------------------------- /helper-functions/README.md: -------------------------------------------------------------------------------- 1 | CMB2 Helper Functions 2 | ========== 3 | 4 | Snippets for working with the [included helper functions](https://github.com/WebDevStudios/CMB2/blob/master/includes/helper-functions.php). 5 | 6 | Related CMB2 issues: 7 | * [#130](https://github.com/WebDevStudios/CMB2/issues/130#issuecomment-68160722) 8 | -------------------------------------------------------------------------------- /helper-functions/helper-functions.php: -------------------------------------------------------------------------------- 1 | autoembed( $content ); 120 | $content = $wp_embed->run_shortcode( $content ); 121 | $content = wpautop( $content ); 122 | $content = do_shortcode( $content ); 123 | 124 | return $content; 125 | } 126 | 127 | /** 128 | * Sample template tag function for outputting a cmb2 file_list. 129 | * 130 | * @link https://github.com/WebDevStudios/CMB2/wiki/Field-Types#sample-function-for-getting-and-outputting-file_list-images Wiki 131 | * 132 | * @param string $meta_key The 'file_list' field meta key. 133 | * @param string $img_size Size of image to display. 134 | */ 135 | function yourprefix_cmb2_output_file_list( $meta_key, $img_size = 'medium' ) { 136 | 137 | // Get the list of files 138 | $files = get_post_meta( get_the_ID(), $meta_key, 1 ); 139 | 140 | echo '
    '; 141 | // Loop through them and output an image 142 | foreach ( (array) $files as $attachment_id => $attachment_url ) { 143 | echo '
    '; 144 | echo wp_get_attachment_image( $attachment_id, $img_size ); 145 | echo '
    '; 146 | } 147 | echo '
    '; 148 | } 149 | -------------------------------------------------------------------------------- /helper-functions/modify-cmb2_metabox_form-format.php: -------------------------------------------------------------------------------- 1 | '
    %3$s
    ', 13 | ) ); 14 | -------------------------------------------------------------------------------- /helper-functions/modify-cmb2_metabox_form-output.php: -------------------------------------------------------------------------------- 1 | cmb_id to retrieve the metabox ID 19 | * 20 | * @return string Possibly modified form output 21 | */ 22 | function myprefix_options_modify_cmb2_metabox_form_format( $form_format, $object_id, $cmb ) { 23 | 24 | if ( 'myprefix_options' == $object_id && 'option_metabox' == $cmb->cmb_id ) { 25 | 26 | $form_format = '
    %3$s
    '; 27 | } 28 | 29 | return $form_format; 30 | } 31 | add_filter( 'cmb2_get_metabox_form_format', 'myprefix_options_modify_cmb2_metabox_form_format', 10, 3 ); 32 | -------------------------------------------------------------------------------- /helper-functions/modify-cmb2_metabox_form-save-button-text.php: -------------------------------------------------------------------------------- 1 | __( 'Save Settings', 'your-textdomain' ), 10 | ) ); 11 | -------------------------------------------------------------------------------- /javascript/README.md: -------------------------------------------------------------------------------- 1 | CMB2 Javascript 2 | ========== 3 | 4 | Custom javascript snippets for interacting with CMB2. -------------------------------------------------------------------------------- /javascript/cmb2-auto-scroll-to-new-group.php: -------------------------------------------------------------------------------- 1 | 18 | 42 | 23 | 96 | 'yourprefix_group_titles_metabox', 18 | 'title' => __( 'Repeating Field Group with Updating Titles', 'cmb2' ), 19 | 'object_types' => array( 'page', ), 20 | 'show_in_rest' => 'read_and_write', 21 | ) ); 22 | 23 | $group_field_id = $cmb_group->add_field( array( 24 | 'id' => 'yourprefix_group_titles_demo', 25 | 'type' => 'group', 26 | 'description' => __( 'Generates reusable form entries', 'cmb2' ), 27 | 'options' => array( 28 | 'group_title' => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number 29 | 'add_button' => __( 'Add Another Entry', 'cmb2' ), 30 | 'remove_button' => __( 'Remove Entry', 'cmb2' ), 31 | 'sortable' => true, // beta 32 | ), 33 | 'after_group' => 'yourprefix_add_js_for_repeatable_titles', 34 | ) ); 35 | 36 | $cmb_group->add_group_field( $group_field_id, array( 37 | 'name' => 'Title', 38 | 'id' => 'title', 39 | 'type' => 'text', 40 | ) ); 41 | 42 | $cmb_group->add_group_field( $group_field_id, array( 43 | 'name' => 'Description', 44 | 'id' => 'description', 45 | 'type' => 'textarea_small', 46 | ) ); 47 | } 48 | add_action( 'cmb2_init', 'yourprefix_register_repeatable_group_field_title_example' ); 49 | 50 | function yourprefix_add_js_for_repeatable_titles() { 51 | add_action( is_admin() ? 'admin_footer' : 'wp_footer', 'yourprefix_add_js_for_repeatable_titles_to_footer' ); 52 | } 53 | 54 | function yourprefix_add_js_for_repeatable_titles_to_footer() { 55 | ?> 56 | 96 | 'field_group_test_one', 15 | 'title' => __( 'Repeating Field Group One', 'cmb2' ), 16 | 'object_types' => array( 'page', ), 17 | 'rows_limit' => 3, // custom attribute to use in our JS 18 | 'groupId' => '_cmb2_repeat_group_one', // custom attribute to use in our JS to retrieve the ID of the group that should be handled 19 | ) ); 20 | 21 | $cmb_two = new_cmb2_box( array( 22 | 'id' => 'field_group_test_two', 23 | 'title' => __( 'Repeating Field Group Two', 'cmb2' ), 24 | 'object_types' => array( 'page', ), 25 | 'rows_limit' => 2, // custom attribute to use in our JS 26 | 'groupId' => '_cmb2_repeat_group_two', // custom attribute to use in our JS to retrieve the ID of the group that should be handled 27 | ) ); 28 | 29 | $group_one = $cmb_one->add_field( array( 30 | 'id' => '_cmb2_repeat_group_one', 31 | 'type' => 'group', 32 | 'description' => __( 'Generates reusable form entries', 'cmb2' ), 33 | 'options' => array( 34 | 'group_title' => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number 35 | 'add_button' => __( 'Add Another Entry', 'cmb2' ), 36 | 'remove_button' => __( 'Remove Entry', 'cmb2' ), 37 | 'sortable' => true, // beta 38 | ), 39 | ) ); 40 | 41 | $cmb_one->add_group_field( $group_one, array( 42 | 'name' => 'Entry Title', 43 | 'id' => 'title', 44 | 'type' => 'text', 45 | ) ); 46 | 47 | $cmb_one->add_group_field( $group_one, array( 48 | 'name' => 'Description', 49 | 'desc' => 'Write a short description for this entry', 50 | 'id' => 'description', 51 | 'type' => 'textarea_small', 52 | ) ); 53 | 54 | $group_two = $cmb_two->add_field( array( 55 | 'id' => '_cmb2_repeat_group_two', 56 | 'type' => 'group', 57 | 'description' => __( 'Generates reusable form entries', 'cmb2' ), 58 | 'options' => array( 59 | 'group_title' => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number 60 | 'add_button' => __( 'Add Another Entry', 'cmb2' ), 61 | 'remove_button' => __( 'Remove Entry', 'cmb2' ), 62 | 'sortable' => true, // beta 63 | ), 64 | ) ); 65 | 66 | $cmb_two->add_group_field( $group_two, array( 67 | 'name' => 'Entry Title', 68 | 'id' => 'title', 69 | 'type' => 'text', 70 | ) ); 71 | 72 | $cmb_two->add_group_field( $group_two, array( 73 | 'name' => 'Description', 74 | 'desc' => 'Write a short description for this entry', 75 | 'id' => 'description', 76 | 'type' => 'textarea_small', 77 | ) ); 78 | 79 | } 80 | add_action( 'cmb2_admin_init', 'js_limited_group_setup', 9999 ); 81 | 82 | 83 | $repeater_metaboxes = array('field_group_test_one','field_group_test_two'); // IDs of the metabox containing the repeater group 84 | 85 | foreach ($repeater_metaboxe as $value) { 86 | add_action( 'cmb2_after_post_form_'.$value, 'js_limit_group_repeat', 10, 2); 87 | } 88 | 89 | function js_limit_group_repeat( $post_id, $cmb ) { 90 | // Grab the custom attribute to determine the limit 91 | $limit = absint( $cmb->prop( 'rows_limit' ) ); 92 | $limit = $limit ? $limit : 0; 93 | $group = $cmb->prop( 'groupId' ) 94 | ?> 95 | 123 | 'test_limit_rows', 14 | 'title' => __( 'Repeating Field Group', 'cmb2' ), 15 | 'object_types' => array( 'page', ), 16 | 'rows_limit' => 3, 17 | ) ); 18 | 19 | $cmb->add_field( array( 20 | 'name' => 'Entry Title', 21 | 'id' => 'text_repeat_test', 22 | 'type' => 'text', 23 | 'repeatable' => true, // Repeatable fields are supported w/in repeatable groups (for most types) 24 | ) ); 25 | } 26 | add_action( 'cmb2_admin_init', 'js_limited_repeat_field_setup', 9999 ); 27 | 28 | function js_limit_field_repeat( $post_id, $cmb ) { 29 | // Grab the custom attribute to determine the limit 30 | $limit = absint( $cmb->prop( 'rows_limit' ) ); 31 | $limit = $limit ? $limit : 0; 32 | ?> 33 | 68 | 'field_group_test', 16 | 'title' => __( 'Repeating Field Group', 'cmb2' ), 17 | 'object_types' => array( 'page', ), 18 | 'rows_limit' => 3, // custom attribute to use in our JS 19 | ) ); 20 | 21 | $group_id = $cmb->add_field( array( 22 | 'id' => '_cmb2_repeat_group', 23 | 'type' => 'group', 24 | 'description' => __( 'Generates reusable form entries', 'cmb2' ), 25 | 'options' => array( 26 | 'group_title' => __( 'Entry {#}', 'cmb2' ), // {#} gets replaced by row number 27 | 'add_button' => __( 'Add Another Entry', 'cmb2' ), 28 | 'remove_button' => __( 'Remove Entry', 'cmb2' ), 29 | 'sortable' => true, // beta 30 | ), 31 | ) ); 32 | 33 | $cmb->add_group_field( $group_id, array( 34 | 'name' => 'Entry Title', 35 | 'id' => 'title', 36 | 'type' => 'text', 37 | ) ); 38 | 39 | $cmb->add_group_field( $group_id, array( 40 | 'name' => 'Description', 41 | 'desc' => 'Write a short description for this entry', 42 | 'id' => 'description', 43 | 'type' => 'textarea_small', 44 | ) ); 45 | } 46 | add_action( 'cmb2_admin_init', 'js_limited_group_setup', 9999 ); 47 | 48 | function js_limit_group_repeat( $post_id, $cmb ) { 49 | // Grab the custom attribute to determine the limit 50 | $limit = absint( $cmb->prop( 'rows_limit' ) ); 51 | $limit = $limit ? $limit : 0; 52 | ?> 53 | 93 | id in the above example. 97 | add_action( 'cmb2_after_post_form_field_group_test', 'js_limit_group_repeat', 10, 2 ); 98 | -------------------------------------------------------------------------------- /metaboxes/README.md: -------------------------------------------------------------------------------- 1 | Metaboxes 2 | ========== 3 | 4 | These are examples of different metabox configurations. -------------------------------------------------------------------------------- /misc/README.md: -------------------------------------------------------------------------------- 1 | Miscellaneous Snippets 2 | ========== 3 | 4 | Random CMB2 and WordPress snippets 5 | -------------------------------------------------------------------------------- /misc/add-wrap-to-group-of-fields.php: -------------------------------------------------------------------------------- 1 | $prefix . 'metabox', 16 | 'title' => esc_html__( 'Test Metabox', 'cmb2' ), 17 | 'object_types' => array( 'page', ), // Post type 18 | ) ); 19 | 20 | // Markup to add a metabox-like toggle box: http://b.ustin.co/12Uba 21 | $advanced_open = ' 22 |
    23 | 24 |

    Toggle Advanced Options

    25 |
    26 | '; 27 | $advanced_close = '
    '; 28 | 29 | $cmb->add_field( array( 30 | 'before_row' => $advanced_open, 31 | 'name' => esc_html__( 'Test Text', 'cmb2' ), 32 | 'desc' => esc_html__( 'field description (optional)', 'cmb2' ), 33 | 'id' => $prefix . 'text', 34 | 'type' => 'text', 35 | ) ); 36 | 37 | $cmb->add_field( array( 38 | 'name' => esc_html__( 'Test Text Small', 'cmb2' ), 39 | 'desc' => esc_html__( 'field description (optional)', 'cmb2' ), 40 | 'id' => $prefix . 'textsmall', 41 | 'type' => 'text_small', 42 | ) ); 43 | 44 | $cmb->add_field( array( 45 | 'name' => esc_html__( 'Test Text Medium', 'cmb2' ), 46 | 'desc' => esc_html__( 'field description (optional)', 'cmb2' ), 47 | 'id' => $prefix . 'textmedium', 48 | 'type' => 'text_medium', 49 | 'after_row' => $advanced_close, 50 | ) ); 51 | 52 | // Optionally do other fields here... 53 | 54 | } 55 | -------------------------------------------------------------------------------- /misc/adding-wordcount-to-cmb2-wysiwyg-field.php: -------------------------------------------------------------------------------- 1 | 'hold-my-wysiwyg', 10 | 'object_types' => array( 'post' ), 11 | ) ); 12 | 13 | $cmb->add_field( array( 14 | 'name' => __( 'WYSIWYG content', 'YOURTEXTDOMAIN' ), 15 | 'id' => 'wysiwyg_content', 16 | 'type' => 'wysiwyg', 17 | 'after' => 'cmb2_wysiwyg_word_counter', 18 | ) ); 19 | 20 | } 21 | add_action( 'cmb2_admin_init', 'yourprefix_feat_img_fields' ); 22 | 23 | /** 24 | * Outputs wordcount for wysiwyg field. 25 | * 26 | * Basically copied from: https://github.com/WordPress/WordPress/blob/3099f4d9edc5f2f2b6ef8becc966135edde909a8/wp-admin/js/post.js#L1219-L1271 27 | * and: https://github.com/WordPress/WordPress/blob/3099f4d9edc5f2f2b6ef8becc966135edde909a8/wp-admin/edit-form-advanced.php#L714 28 | */ 29 | function cmb2_wysiwyg_word_counter( $args, $field ) { 30 | wp_enqueue_script( 'word-count', array( 'jquery', 'underscore', 'word-count' ) ); 31 | ?> 32 | 33 |

    0' ); ?>

    34 | 35 | 94 | 6 | * @link https://joebuckle.me/quickie/wordpress-add-options-to-post-admin-publish-meta-box/ 7 | * @link https://github.com/CMB2/CMB2-Snippet-Library/blob/master/misc/outputting-forms-outside-metaboxes.php 8 | * @link https://codex.wordpress.org/Plugin_API/Action_Reference/post_submitbox_misc_actions 9 | */ 10 | 11 | /** 12 | * Register the CMB2 metabox. 13 | */ 14 | function yourprefix_cmb2_fields() { 15 | $prefix = '_yourprefix_'; 16 | 17 | $cmb = new_cmb2_box( [ 18 | 'id' => $prefix . 'publish_box', 19 | 'object_types' => [ 'post' ], // Any public post type. 20 | 'show_names' => false, // Disables the display for the CMB2 label. 21 | ] ); 22 | 23 | $cmb->add_field( [ 24 | 'before' => '', // Output the label w/o the default CMB2 styles. 25 | 'id' => $prefix . 'field', 26 | 'desc' => __( 'Field description (optional)', 'cmb2' ), 27 | 'type' => 'text', // Any valid CMB2 field type. 28 | 'attributes' => [ 29 | 'style' => 'max-width: 80%;', // Cleans up the default CMB2 styles a bit. Move to stylesheet? 30 | ] 31 | ] ); 32 | } 33 | 34 | add_action( 'post_submitbox_misc_actions', 'yourprefix_filter_publish_box' ); 35 | 36 | /** 37 | * Display the CMB2 form in the WordPress publish metabox. 38 | * 39 | * @param object $post The WP_Post object. 40 | */ 41 | function yourprefix_filter_publish_box( $post ) { 42 | $cmb = cmb2_get_metabox( '_yourprefix_publish_box' ); // Must match the box ID. 43 | 44 | if ( in_array( $post->post_type, $cmb->prop( 'object_types' ), true ) ) { 45 | $cmb->show_form(); 46 | } 47 | } 48 | 49 | add_action( 'cmb2_admin_init', 'yourprefix_cmb2_fields' ); 50 | -------------------------------------------------------------------------------- /misc/helper-functions.php: -------------------------------------------------------------------------------- 1 | value data for saving. Likely $_POST data. 15 | */ 16 | function cmb2_save_metabox_fields_data( $meta_box_id, $object_id, array $data_to_save ) { 17 | $cmb = cmb2_get_metabox( $meta_box_id, $object_id ); 18 | $cmb->save_fields( $object_id, $cmb->object_type(), $data_to_save ); 19 | } 20 | 21 | /** 22 | * Returns array of sanitized field values (without saving them) from 23 | * provided array of values (Likely $_POST data.) 24 | * 25 | * Combined with the metabox `'save_fields' => false` config option, you can use this to 26 | * save the box data somewhere else. 27 | * 28 | * @link https://wordpress.org/support/topic/sanitizing-data-outside-metabox-context Forum post 29 | * 30 | * @param mixed $meta_box_id Metabox ID (or metabox config array) 31 | * @param int $object_id ID of post/user/comment/options-page to save the data against 32 | * @param array $data_to_save Array of key => value data for saving. Likely $_POST data. 33 | */ 34 | function cmb2_get_metabox_sanitized_values( $meta_box_id, array $data_to_save ) { 35 | $cmb = cmb2_get_metabox( $meta_box_id ); 36 | $cmb->get_sanitized_values( $data_to_save ); 37 | } 38 | -------------------------------------------------------------------------------- /misc/outputting-cmb2-fields-in-featured-image-metabox.php: -------------------------------------------------------------------------------- 1 | 'feat-image-fields', 10 | 'object_types' => array( 'post' ), 11 | ) ); 12 | 13 | $cmb->add_field( array( 14 | 'name' => 'Featured Image Position', 15 | 'id' => 'feat_img_placement', 16 | 'type' => 'select', 17 | 'options' => array( 18 | '' => 'Center', // The default -- no value. Keeps out of the database. 19 | 'left' => 'Left', 20 | 'right' => 'Right', 21 | ), 22 | 'before' => '', 29 | ) ); 30 | 31 | } 32 | add_action( 'cmb2_admin_init', 'yourprefix_feat_img_fields' ); 33 | 34 | function yourprefix_feat_img_output_fields( $content, $post_id, $thumbnail_id ) { 35 | $cmb = cmb2_get_metabox( 'feat-image-fields' ); 36 | 37 | if ( $cmb && in_array( get_post_type(), $cmb->prop( 'object_types' ), 1 ) ) { 38 | ob_start(); 39 | $cmb->show_form(); 40 | // grab the data from the output buffer and add it to our $content variable 41 | $content .= ob_get_clean(); 42 | } 43 | 44 | return $content; 45 | } 46 | add_filter( 'admin_post_thumbnail_html', 'yourprefix_feat_img_output_fields', 10, 3 ); 47 | -------------------------------------------------------------------------------- /misc/outputting-forms-outside-metaboxes.php: -------------------------------------------------------------------------------- 1 | '_yourprefix_display_title', 13 | 'object_types' => array( 'page' ), 14 | //'title' => '', omit the 'title' field to keep the normal wp metabox from displaying 15 | ) ); 16 | 17 | $cmb->add_field( array( 18 | 'name' => 'Display title for this page?', 19 | 'id' => '_yourprefix_display_title', 20 | 'type' => 'checkbox', 21 | ) ); 22 | 23 | $cmb->add_field( array( 24 | 'name' => 'A textarea', 25 | 'id' => '_yourprefix_display_title_text', 26 | 'type' => 'textarea', 27 | ) ); 28 | 29 | } 30 | add_action( 'cmb2_admin_init', 'yourprefix_register_cmb2_fields' ); 31 | 32 | 33 | /** 34 | * Display checkbox metabox below title field 35 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L517-L523 36 | */ 37 | function yourprefix_output_custom_mb_location() { 38 | $cmb = cmb2_get_metabox( '_yourprefix_display_title' ); 39 | 40 | if ( in_array( get_post_type(), $cmb->prop( 'object_types' ), 1 ) ) { 41 | $cmb->show_form(); 42 | } 43 | } 44 | add_action( 'edit_form_after_title', 'yourprefix_output_custom_mb_location' ); 45 | 46 | /** 47 | * More hooks in the post-editor screen as of 4.1 48 | */ 49 | 50 | /** 51 | * Display checkbox metabox below wysiwyg editor field 52 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L560-L567 53 | */ 54 | // add_action( 'edit_form_after_editor', 'yourprefix_output_custom_mb_location' ); 55 | 56 | /** 57 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L217-L225 58 | */ 59 | // add_action( 'dbx_post_advanced', 'yourprefix_output_custom_mb_location' ); 60 | 61 | /** 62 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L241-L249 63 | */ 64 | // add_action( 'add_meta_boxes', 'yourprefix_output_custom_mb_location' ); 65 | 66 | /** 67 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L416-L423 68 | */ 69 | // add_action( 'post_edit_form_tag', 'yourprefix_output_custom_mb_location' ); 70 | 71 | /** 72 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L447-L456 73 | */ 74 | // add_action( 'edit_form_top', 'yourprefix_output_custom_mb_location' ); 75 | 76 | /** 77 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L480-L487 78 | */ 79 | // add_action( 'edit_form_before_permalink', 'yourprefix_output_custom_mb_location' ); 80 | 81 | /** 82 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L516-L523 83 | */ 84 | // add_action( 'edit_form_after_title', 'yourprefix_output_custom_mb_location' ); 85 | 86 | /** 87 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L560-L567 88 | */ 89 | // add_action( 'edit_form_after_editor', 'yourprefix_output_custom_mb_location' ); 90 | 91 | /** 92 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L574-L597 93 | */ 94 | // add_action( 'submitpost_box', 'yourprefix_output_custom_mb_location' ); 95 | // add_action( 'submitpage_box', 'yourprefix_output_custom_mb_location' ); 96 | 97 | /** 98 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L609-L628 99 | */ 100 | // add_action( 'edit_form_advanced', 'yourprefix_output_custom_mb_location' ); 101 | // add_action( 'edit_page_form', 'yourprefix_output_custom_mb_location' ); 102 | 103 | /** 104 | * @link https://github.com/WordPress/WordPress/blob/56d6682461be82da1a3bafc454dad2c9da451a38/wp-admin/edit-form-advanced.php#L636-L643 105 | */ 106 | // add_action( 'dbx_post_sidebar', 'yourprefix_output_custom_mb_location' ); 107 | -------------------------------------------------------------------------------- /misc/replace-wp-excerpt-with-cmb2-field.php: -------------------------------------------------------------------------------- 1 | 'cmb2_excerpt', 11 | 'title' => 'Excerpt', 12 | 'object_types' => array( 'post', ), // Post type 13 | // 'context' => 'side', 14 | ) ); 15 | 16 | $cmb->add_field( array( 17 | /* 18 | * As long as the 'id' matches the name field of the regular WP field, 19 | * WP will handle the saving for you. 20 | */ 21 | 'id' => 'excerpt', 22 | 'name' => 'Excerpt', 23 | 'desc' => 'Excerpts are optional hand-crafted summaries of your content that can be used in your theme. Learn more about manual excerpts.', 24 | 'type' => 'textarea', 25 | 'escape_cb' => false, 26 | ) ); 27 | 28 | } 29 | add_action( 'cmb2_admin_init', 'cmb2_register_excerpt_replacement_box' ); 30 | 31 | 32 | /** 33 | * Remove the default WordPress excerpt field. 34 | */ 35 | function cmb2_admin_hide_excerpt_field() { 36 | add_action( 'add_meta_boxes', '_cmb2_admin_hide_excerpt_field' ); 37 | } 38 | add_filter( 'admin_init', 'cmb2_admin_hide_excerpt_field' ); 39 | 40 | function _cmb2_admin_hide_excerpt_field() { 41 | $screen = get_current_screen(); 42 | 43 | if ( isset( $screen->post_type ) && 'post' === $screen->post_type ) { 44 | remove_meta_box( 'postexcerpt', null, 'normal' ); 45 | } 46 | } 47 | 48 | 49 | /** 50 | * Override the WordPress Excerpt field 51 | */ 52 | function cmb2_override_excerpt_display( $data, $post_id ) { 53 | return get_post_field( 'post_excerpt', $post_id ); 54 | } 55 | add_filter( 'cmb2_override_excerpt_meta_value', 'cmb2_override_excerpt_display', 10, 2 ); 56 | 57 | /* 58 | * WP will handle the saving for us, so don't save to meta. 59 | */ 60 | add_filter( 'cmb2_override_excerpt_meta_save', '__return_true' ); 61 | -------------------------------------------------------------------------------- /misc/replace-wp-title-content-thumbnail-with-cmb2-fields.php: -------------------------------------------------------------------------------- 1 | 'CPT', 12 | 'singular_name' => 'CPT', 13 | 'menu_name' => 'CPT', 14 | 'parent_item_colon' => 'Parent CPT', 15 | 'all_items' => 'All CPT', 16 | 'view_item' => 'View CPT', 17 | 'add_new_item' => 'Add New CPT', 18 | 'add_new' => 'Add New', 19 | 'edit_item' => 'Edit CPT', 20 | 'update_item' => 'Update CPT', 21 | 'search_items' => 'Search CPT', 22 | 'not_found' => 'Not Found', 23 | 'not_found_in_trash' => 'Not found in Trash', 24 | ); 25 | 26 | register_post_type( 'cpt', array( 27 | 'labels' => $labels, 28 | 'supports' => array( 'title' ), 29 | 'supports' => array( '' ), 30 | 'public' => true, 31 | 'has_archive' => true, 32 | ) ); 33 | } 34 | add_action( 'init', 'register_test_cpt' ); 35 | 36 | function register_test_cpt_metabox() { 37 | 38 | $cmb_subsub = new_cmb2_box( array ( 39 | 'id' => 'metabox', 40 | 'title' => 'Edit', 41 | 'object_types' => array( 'cpt' ), 42 | ) ); 43 | 44 | $cmb_subsub->add_field( array( 45 | 'id' => 'post_title', // Saves to WP post title, allows the_title() 46 | 'name' => 'Title', 47 | 'desc' => 'Provide a title.', 48 | 'default' => '', 49 | 'type' => 'text', 50 | ) ); 51 | 52 | $cmb_subsub->add_field( array( 53 | 'id' => 'post_content', // Saves to WP post content, allows the_content() 54 | 'name' => 'Description', 55 | 'desc' => 'Enter a brief description', 56 | 'type' => 'textarea', // wysiwyg is problematic when replacing post_content 57 | ) ); 58 | 59 | $cmb_subsub->add_field( array( 60 | 'id' => '_thumbnail', // Saves to WP post thumbnail, allows the_post_thumbnail() 61 | 'name' => 'Image', 62 | 'desc' => 'Upload/Select an image.', 63 | 'type' => 'file', 64 | 'options' => array( 65 | 'url' => false, 66 | ), 67 | 'text' => array( 68 | 'add_upload_file_text' => 'Add Image' 69 | ), 70 | ) ); 71 | 72 | }; 73 | add_action( 'cmb2_admin_init', 'register_test_cpt_metabox' ); 74 | 75 | /* 76 | * Override the title/content field retrieval so CMB2 doesn't look in post-meta. 77 | */ 78 | function cmb2_override_post_title_display( $data, $post_id ) { 79 | return get_post_field( 'post_title', $post_id ); 80 | } 81 | function cmb2_override_post_content_display( $data, $post_id ) { 82 | return get_post_field( 'post_content', $post_id ); 83 | } 84 | add_filter( 'cmb2_override_post_title_meta_value', 'cmb2_override_post_title_display', 10, 2 ); 85 | add_filter( 'cmb2_override_post_content_meta_value', 'cmb2_override_post_content_display', 10, 2 ); 86 | 87 | /* 88 | * WP will handle the saving for us, so don't save title/content to meta. 89 | */ 90 | add_filter( 'cmb2_override_post_title_meta_save', '__return_true' ); 91 | add_filter( 'cmb2_override_post_content_meta_save', '__return_true' ); 92 | -------------------------------------------------------------------------------- /modified-field-types/README.md: -------------------------------------------------------------------------------- 1 | Modified Field Types 2 | ========== 3 | 4 | These 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). -------------------------------------------------------------------------------- /modified-field-types/modify-button-text-for-file-type.php: -------------------------------------------------------------------------------- 1 | add_field( array( 11 | 'name' => 'Image', 12 | 'desc' => 'Upload an image.', 13 | 'id' => '_jt_cmb2_image', 14 | 'type' => 'file', 15 | 'options' => array( 16 | 'add_upload_file_text' => __( 'Add or Upload Image', 'jt_cmb2' ), 17 | ), 18 | ) ); 19 | -------------------------------------------------------------------------------- /modified-field-types/readonly-field-type.php: -------------------------------------------------------------------------------- 1 | add_field( array( 10 | 'name' => 'Read Only', 11 | 'description' => 'The value of this input should be saved somewhere else.', 12 | 'id' => '_jtcmb2_readonly', 13 | 'type' => 'text', 14 | 'save_field' => false, // Otherwise CMB2 will end up removing the value. 15 | 'attributes' => array( 16 | 'readonly' => 'readonly', 17 | 'disabled' => 'disabled', 18 | ), 19 | ) ); 20 | -------------------------------------------------------------------------------- /options-and-settings-pages/README.md: -------------------------------------------------------------------------------- 1 | Options and Settings Pages 2 | ========== 3 | 4 | These snippets demonstrate how to create options page metaboxes or hook into genesis settings metaboxes. 5 | 6 | There are also examples for how you can retrieve an option, using `myprefix_get_option( 'test_text' )`. 7 | 8 | Obviously replace all instances of `myprefix` with a unique project-specific prefix. 9 | 10 | [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. 11 | -------------------------------------------------------------------------------- /options-and-settings-pages/add-cmb2-settings-to-other-settings-pages.php: -------------------------------------------------------------------------------- 1 | hooks(); 48 | } 49 | return self::$instance; 50 | } 51 | 52 | /** 53 | * Initiate our hooks 54 | * @since 0.1.0 55 | */ 56 | public function hooks() { 57 | add_action( 'cmb2_admin_init', array( $this, 'register_metabox' ) ); 58 | add_action( 'current_screen', array( $this, 'maybe_save' ) ); 59 | add_filter( 'admin_footer' , array( $this , 'maybe_hookup_fields' ), 2 /* Early before all scripts are output. */ ); 60 | } 61 | 62 | /** 63 | * Add the options metabox to the array of metaboxes 64 | * @since 0.1.0 65 | */ 66 | function register_metabox() { 67 | $cmb = new_cmb2_box( array( 68 | 'id' => $this->metabox_id, 69 | 'hookup' => false, 70 | 'object_types' => array( 'options-page' ), 71 | ) ); 72 | 73 | // Set our CMB2 fields 74 | 75 | $cmb->add_field( array( 76 | 'name' => __( 'Test Text', 'myprefix' ), 77 | 'desc' => __( 'field description (optional)', 'myprefix' ), 78 | 'id' => 'test_text', 79 | 'type' => 'text', 80 | // 'default' => 'Default Text', 81 | ) ); 82 | 83 | $cmb->add_field( array( 84 | 'name' => __( 'Test Color Picker', 'myprefix' ), 85 | 'desc' => __( 'field description (optional)', 'myprefix' ), 86 | 'id' => 'test_colorpicker', 87 | 'type' => 'colorpicker', 88 | 'default' => '#bada55', 89 | ) ); 90 | 91 | } 92 | 93 | /** 94 | * Register our setting to WP 95 | * @since 0.1.0 96 | */ 97 | public function maybe_save() { 98 | if ( empty( $_POST ) ) { 99 | return; 100 | } 101 | 102 | $url = wp_get_referer(); 103 | // Check if our screen id is in the referrer url. 104 | if ( false === strpos( $url, $this->screen_id ) ) { 105 | return; 106 | } 107 | 108 | // Hook into whitelist_options as we know it's only called if the default save-checks have finished. 109 | add_filter( 'whitelist_options', array( $this, 'save_our_options' ) ); 110 | } 111 | 112 | /** 113 | * Simply used as a options.php life-cycle hook to save our settings 114 | * (since there doesn't appear to be any proper hooks) 115 | * 116 | * @since 0.1.0 117 | * 118 | * @param array $whitelist_options 119 | * 120 | * @return array 121 | */ 122 | public function save_our_options( $whitelist_options ) { 123 | $cmb = cmb2_get_metabox( $this->metabox_id, $this->key ); 124 | if ( $cmb ) { 125 | 126 | $hookup = new CMB2_hookup( $cmb ); 127 | 128 | if ( $hookup->can_save( 'options-page' ) ) { 129 | $cmb->save_fields( $this->key, 'options-page', $_POST ); 130 | } 131 | } 132 | 133 | // Our saving is done, so cleanup. 134 | remove_filter( 'whitelist_options', array( $this, 'save_our_options' ) ); 135 | 136 | return $whitelist_options; 137 | } 138 | 139 | /** 140 | * Maybe hookup our CMB2 fields. 141 | * 142 | * @since 0.1.0 143 | */ 144 | public function maybe_hookup_fields() { 145 | $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : (object) array( 'id' => null ); 146 | 147 | // Only show on our screen. 148 | if ( $this->screen_id !== $screen->id ) { 149 | return; 150 | } 151 | 152 | CMB2_hookup::enqueue_cmb_css(); 153 | $this->admin_page_display(); 154 | } 155 | 156 | /** 157 | * CMB2 fields output 158 | * Wile hide by default in the footer, then use JS to move it inside the form. Hacky, yep. 159 | * 160 | * @since 0.1.0 161 | */ 162 | public function admin_page_display() { 163 | ?> 164 | 167 | 173 | {$field}; 186 | } 187 | 188 | throw new Exception( 'Invalid property: ' . $field ); 189 | } 190 | 191 | } 192 | 193 | /** 194 | * Helper function to get/return the Prefix_Add_CMB2_To_Settings_Page object 195 | * @since 0.1.0 196 | * @return Prefix_Add_CMB2_To_Settings_Page object 197 | */ 198 | function myprefix_cmb2_on_settings() { 199 | return Prefix_Add_CMB2_To_Settings_Page::get_instance(); 200 | } 201 | 202 | 203 | /** 204 | * Wrapper function around cmb2_get_option 205 | * @since 0.1.0 206 | * @param string $key Options array key 207 | * @param mixed $default Optional default value 208 | * @return mixed Option value 209 | */ 210 | function myprefix_get_option( $key = '', $default = false ) { 211 | if ( function_exists( 'cmb2_get_option' ) ) { 212 | // Use cmb2_get_option as it passes through some key filters. 213 | return cmb2_get_option( myprefix_cmb2_on_settings()->key, $key, $default ); 214 | } 215 | // Fallback to get_option if CMB2 is not loaded yet. 216 | $opts = get_option( myprefix_cmb2_on_settings()->key, $default ); 217 | $val = $default; 218 | if ( 'all' == $key ) { 219 | $val = $opts; 220 | } elseif ( is_array( $opts ) && array_key_exists( $key, $opts ) && false !== $opts[ $key ] ) { 221 | $val = $opts[ $key ]; 222 | } 223 | return $val; 224 | } 225 | 226 | // Get it started 227 | myprefix_cmb2_on_settings(); -------------------------------------------------------------------------------- /options-and-settings-pages/custom-display-callback.php: -------------------------------------------------------------------------------- 1 | 'yourprefix_theme_options_page', 10 | 'title' => 'Theme Options', 11 | 'object_types' => array( 'options-page' ), 12 | 'option_key' => 'yourprefix_theme_options', 13 | 'icon_url' => 'dashicons-palmtree', 14 | 'display_cb' => 'yourprefix_theme_options_page_output', // Override the options-page form output (CMB2_Hookup::options_page_output()). 15 | 'description' => 'Custom description', // Will be displayed via our display_cb. 16 | ) ); 17 | 18 | $cmb_options->add_field( array( 19 | 'name' => 'Site Background Color', 20 | 'desc' => 'field description (optional)', 21 | 'id' => 'bg_color', 22 | 'type' => 'colorpicker', 23 | 'default' => '#ffffff', 24 | ) ); 25 | 26 | } 27 | 28 | function yourprefix_theme_options_page_output( $hookup ) { 29 | // Output custom markup for the options-page. 30 | ?> 31 |
    32 | cmb->prop( 'title' ) ) : ?> 33 |

    cmb->prop( 'title' ) ); ?>

    34 | 35 | cmb->prop( 'description' ) ) : ?> 36 |

    cmb->prop( 'description' ) ); ?>

    37 | 38 |
    39 | 40 | options_page_metabox(); ?> 41 | cmb->prop( 'save_button' ) ), 'primary', 'submit-cmb' ); ?> 42 |
    43 |
    44 | hooks(); 71 | } 72 | 73 | return self::$instances[ $post_type ]; 74 | } 75 | 76 | /** 77 | * Constructor 78 | * 79 | * @since 0.1.0 80 | * 81 | * @param string $post_type Post type slug. 82 | */ 83 | protected function __construct( $post_type ) { 84 | $this->post_type = $post_type; 85 | $this->admin_hook = sprintf( $this->admin_hook, $post_type ); 86 | $this->key = sprintf( $this->key, $post_type ); 87 | $this->metabox_id = sprintf( $this->metabox_id, $post_type ); 88 | } 89 | 90 | /** 91 | * Initiate our hooks 92 | * 93 | * @since 0.1.0 94 | */ 95 | public function hooks() { 96 | add_action( 'init', array( $this, 'init' ) ); 97 | add_action( 'admin_menu', array( $this, 'admin_hooks' ) ); 98 | add_action( 'cmb2_admin_init', array( $this, 'init_metabox' ) ); 99 | } 100 | 101 | 102 | /** 103 | * Initiate admin hooks. 104 | * 105 | * @since 0.1.0 106 | */ 107 | public function init() { 108 | // Add custom archive support for CPT. 109 | add_post_type_support( $this->post_type, 'genesis-cpt-archives-settings' ); 110 | } 111 | 112 | /** 113 | * Add admin hooks. 114 | * 115 | * @since 0.1.0 116 | */ 117 | public function admin_hooks() { 118 | // Include CMB CSS in the head to avoid FOUC. 119 | add_action( "admin_print_styles-{$this->admin_hook}", array( 'CMB2_hookup', 'enqueue_cmb_css' ) ); 120 | 121 | // Hook into the genesis cpt settings save and add in the CMB2 sanitized values. 122 | add_filter( "sanitize_option_genesis-cpt-archive-settings-{$this->post_type}", array( $this, 'add_sanitized_values' ), 999 ); 123 | 124 | // Hook up our Genesis metabox. 125 | add_action( 'genesis_cpt_archives_settings_metaboxes', array( $this, 'add_meta_box' ) ); 126 | } 127 | 128 | /** 129 | * Hook up our Genesis metabox. 130 | * 131 | * @since 0.1.0 132 | */ 133 | public function add_meta_box() { 134 | $cmb = $this->init_metabox(); 135 | add_meta_box( 136 | $cmb->cmb_id, 137 | $cmb->prop( 'title' ), 138 | array( $this, 'output_metabox' ), 139 | $this->admin_hook, 140 | $cmb->prop( 'context' ), 141 | $cmb->prop( 'priority' ) 142 | ); 143 | } 144 | 145 | /** 146 | * Output our Genesis metabox. 147 | * 148 | * @since 0.1.0 149 | */ 150 | public function output_metabox() { 151 | $cmb = $this->init_metabox(); 152 | $cmb->show_form( $cmb->object_id(), $cmb->object_type() ); 153 | } 154 | 155 | /** 156 | * If saving the cpt settings option, add the CMB2 sanitized values. 157 | * 158 | * @since 0.1.0 159 | * 160 | * @param array $new_value Array of values for the setting. 161 | * 162 | * @return array Updated array of values for the setting. 163 | */ 164 | public function add_sanitized_values( $new_value ) { 165 | if ( ! empty( $_POST ) ) { 166 | $cmb = $this->init_metabox(); 167 | 168 | $new_value = array_merge( 169 | $new_value, 170 | $cmb->get_sanitized_values( $_POST ) 171 | ); 172 | } 173 | 174 | return $new_value; 175 | } 176 | 177 | /** 178 | * Register our Genesis metabox and return the CMB2 instance. 179 | * 180 | * @since 0.1.0 181 | * 182 | * @return CMB2 instance. 183 | */ 184 | public function init_metabox() { 185 | if ( null !== $this->cmb ) { 186 | return $this->cmb; 187 | } 188 | 189 | $this->cmb = cmb2_get_metabox( array( 190 | 'id' => $this->metabox_id, 191 | 'title' => __( 'I\'m a Genesis Archive Settings CMB2 metabox', 'myprefix' ), 192 | 'hookup' => false, // We'll handle ourselves. (add_sanitized_values()) 193 | 'cmb_styles' => false, // We'll handle ourselves. (admin_hooks()) 194 | 'context' => 'main', // Important for Genesis. 195 | // 'priority' => 'low', // Defaults to 'high'. 196 | 'object_types' => array( $this->admin_hook ), 197 | 'show_on' => array( 198 | // These are important, don't remove. 199 | 'key' => 'options-page', 200 | 'value' => array( $this->key ), 201 | ), 202 | ), $this->key, 'options-page' ); 203 | 204 | // Set our CMB2 fields. 205 | $this->cmb->add_field( array( 206 | 'name' => __( 'Test Text', 'myprefix' ), 207 | 'desc' => __( 'field description (optional)', 'myprefix' ), 208 | 'id' => 'test_text', 209 | 'type' => 'text', 210 | // 'default' => 'Default Text', 211 | ) ); 212 | 213 | $this->cmb->add_field( array( 214 | 'name' => __( 'Test Color Picker', 'myprefix' ), 215 | 'desc' => __( 'field description (optional)', 'myprefix' ), 216 | 'id' => 'test_colorpicker', 217 | 'type' => 'colorpicker', 218 | 'default' => '#bada55', 219 | ) ); 220 | 221 | return $this->cmb; 222 | } 223 | 224 | /** 225 | * Public getter method for retrieving protected/private variables 226 | * 227 | * @since 0.1.0 228 | * 229 | * @param string $field Field to retrieve. 230 | * 231 | * @throws Exception Throws an exception if the field is invalid. 232 | * 233 | * @return mixed Field value or exception is thrown 234 | */ 235 | public function __get( $field ) { 236 | // Allowed fields to retrieve. 237 | if ( 'cmb' === $field ) { 238 | return $this->init_metabox(); 239 | } 240 | 241 | if ( in_array( $field, array( 'metabox_id', 'post_type', 'admin_hook', 'key' ), true ) ) { 242 | return $this->{$field}; 243 | } 244 | 245 | throw new Exception( 'Invalid property: ' . $field ); 246 | } 247 | 248 | } 249 | 250 | /** 251 | * Helper function to get/return the Myprefix_Genesis_CPT_Settings_Metabox object. 252 | * 253 | * @since 0.1.0 254 | * 255 | * @param string $post_type Post type slug. 256 | * 257 | * @return Myprefix_Genesis_CPT_Settings_Metabox object 258 | */ 259 | function myprefix_genesis_cpt_settings( $post_type ) { 260 | return Myprefix_Genesis_CPT_Settings_Metabox::get_instance( $post_type ); 261 | } 262 | 263 | // Get it started. 264 | // myprefix_genesis_cpt_settings( 'custom-post-type-slug' ); 265 | -------------------------------------------------------------------------------- /options-and-settings-pages/genesis-settings-metabox.php: -------------------------------------------------------------------------------- 1 | hooks(); 64 | } 65 | 66 | return self::$instance; 67 | } 68 | 69 | /** 70 | * Constructor 71 | * 72 | * @since 0.1.0 73 | */ 74 | protected function __construct() { 75 | } 76 | 77 | /** 78 | * Initiate our hooks 79 | * 80 | * @since 0.1.0 81 | */ 82 | public function hooks() { 83 | add_action( 'admin_menu', array( $this, 'admin_hooks' ) ); 84 | add_action( 'cmb2_admin_init', array( $this, 'init_metabox' ) ); 85 | } 86 | 87 | /** 88 | * Add menu options page 89 | * 90 | * @since 0.1.0 91 | */ 92 | public function admin_hooks() { 93 | // Include CMB CSS in the head to avoid FOUC. 94 | add_action( "admin_print_styles-{$this->admin_hook}", array( 'CMB2_hookup', 'enqueue_cmb_css' ) ); 95 | 96 | // Hook into the genesis cpt setttings save and add in the CMB2 sanitized values. 97 | add_filter( "sanitize_option_{$this->key}", array( $this, 'add_sanitized_values' ), 999 ); 98 | 99 | // Hook up our Genesis metabox. 100 | add_action( 'genesis_theme_settings_metaboxes', array( $this, 'add_meta_box' ) ); 101 | } 102 | 103 | 104 | /** 105 | * Hook up our Genesis metabox. 106 | * 107 | * @since 0.1.0 108 | */ 109 | public function add_meta_box() { 110 | $cmb = $this->init_metabox(); 111 | add_meta_box( 112 | $cmb->cmb_id, 113 | $cmb->prop( 'title' ), 114 | array( $this, 'output_metabox' ), 115 | $this->admin_hook, 116 | $cmb->prop( 'context' ), 117 | $cmb->prop( 'priority' ) 118 | ); 119 | } 120 | 121 | /** 122 | * Output our Genesis metabox. 123 | * 124 | * @since 0.1.0 125 | */ 126 | public function output_metabox() { 127 | $cmb = $this->init_metabox(); 128 | $cmb->show_form( $cmb->object_id(), $cmb->object_type() ); 129 | } 130 | 131 | /** 132 | * If saving the cpt settings option, add the CMB2 sanitized values. 133 | * 134 | * @since 0.1.0 135 | * 136 | * @param array $new_value Array of values for the setting. 137 | * 138 | * @return array Updated array of values for the setting. 139 | */ 140 | public function add_sanitized_values( $new_value ) { 141 | if ( ! empty( $_POST ) ) { 142 | $cmb = $this->init_metabox(); 143 | 144 | $new_value = array_merge( 145 | $new_value, 146 | $cmb->get_sanitized_values( $_POST ) 147 | ); 148 | } 149 | 150 | return $new_value; 151 | } 152 | 153 | /** 154 | * Register our Genesis metabox and return the CMB2 instance. 155 | * 156 | * @since 0.1.0 157 | * 158 | * @return CMB2 instance. 159 | */ 160 | public function init_metabox() { 161 | if ( null !== $this->cmb ) { 162 | return $this->cmb; 163 | } 164 | 165 | $this->cmb = cmb2_get_metabox( array( 166 | 'id' => $this->metabox_id, 167 | 'title' => __( 'I\'m a Genesis Settings CMB2 metabox', 'myprefix' ), 168 | 'hookup' => false, // We'll handle ourselves. (add_sanitized_values()) 169 | 'cmb_styles' => false, // We'll handle ourselves. (admin_hooks()) 170 | 'context' => 'main', // Important for Genesis. 171 | // 'priority' => 'low', // Defaults to 'high'. 172 | 'object_types' => array( $this->admin_hook ), 173 | 'show_on' => array( 174 | // These are important, don't remove. 175 | 'key' => 'options-page', 176 | 'value' => array( $this->key ), 177 | ), 178 | ), $this->key, 'options-page' ); 179 | 180 | // Set our CMB2 fields. 181 | $this->cmb->add_field( array( 182 | 'name' => __( 'Test Text', 'myprefix' ), 183 | 'desc' => __( 'field description (optional)', 'myprefix' ), 184 | 'id' => 'test_text', 185 | 'type' => 'text', 186 | // 'default' => 'Default Text', 187 | ) ); 188 | 189 | $this->cmb->add_field( array( 190 | 'name' => __( 'Test Color Picker', 'myprefix' ), 191 | 'desc' => __( 'field description (optional)', 'myprefix' ), 192 | 'id' => 'test_colorpicker', 193 | 'type' => 'colorpicker', 194 | 'default' => '#bada55', 195 | ) ); 196 | 197 | return $this->cmb; 198 | } 199 | 200 | /** 201 | * Public getter method for retrieving protected/private variables. 202 | * 203 | * @since 0.1.0 204 | * 205 | * @param string $field Field to retrieve. 206 | * 207 | * @throws Exception Throws an exception if the field is invalid. 208 | * 209 | * @return mixed Field value or exception is thrown 210 | */ 211 | public function __get( $field ) { 212 | if ( 'cmb' === $field ) { 213 | return $this->init_metabox(); 214 | } 215 | 216 | // Allowed fields to retrieve. 217 | if ( in_array( $field, array( 'key', 'admin_page', 'metabox_id', 'admin_hook' ), true ) ) { 218 | return $this->{$field}; 219 | } 220 | 221 | throw new Exception( 'Invalid property: ' . $field ); 222 | } 223 | 224 | } 225 | 226 | /** 227 | * Helper function to get/return the Myprefix_Genesis_Settings_Metabox object 228 | * 229 | * @since 0.1.0 230 | * 231 | * @return Myprefix_Genesis_Settings_Metabox object 232 | */ 233 | function myprefix_genesis_settings_metabox() { 234 | return Myprefix_Genesis_Settings_Metabox::get_instance(); 235 | } 236 | 237 | // Get it started. 238 | myprefix_genesis_settings_metabox(); 239 | -------------------------------------------------------------------------------- /options-and-settings-pages/network-options-cmb.php: -------------------------------------------------------------------------------- 1 | 'myprefix_network_option_metabox', 20 | 'title' => esc_html__( 'Network Setting', 'myprefix' ), 21 | 'object_types' => array( 'options-page' ), 22 | 23 | /* 24 | * The following parameters are specific to the options-page box 25 | * Several of these parameters are passed along to add_menu_page()/add_submenu_page(). 26 | */ 27 | 28 | 'option_key' => 'myprefix_network_options', // The option key and admin menu page slug. 29 | // 'icon_url' => 'dashicons-palmtree', // Menu icon. Only applicable if 'parent_slug' is left empty. 30 | // 'menu_title' => esc_html__( 'Options', 'myprefix' ), // Falls back to 'title' (above). 31 | // 'parent_slug' => 'themes.php', // Make options page a submenu item of the themes menu. 32 | // 'capability' => 'manage_options', // Cap required to view options-page. 33 | // 'position' => 1, // Menu position. Only applicable if 'parent_slug' is left empty. 34 | 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page. 35 | // 'display_cb' => false, // Override the options-page form output (CMB2_Hookup::options_page_output()). 36 | // 'save_button' => esc_html__( 'Save Theme Options', 'myprefix' ), // The text for the options-page save button. Defaults to 'Save'. 37 | ) ); 38 | 39 | /* 40 | * Options fields ids only need 41 | * to be unique within this box. 42 | * Prefix is not needed. 43 | */ 44 | 45 | $cmb_options->add_field( array( 46 | 'name' => __( 'Test Text', 'myprefix' ), 47 | 'desc' => __( 'field description (optional)', 'myprefix' ), 48 | 'id' => 'test_text', 49 | 'type' => 'text', 50 | 'default' => 'Default Text', 51 | ) ); 52 | 53 | $cmb_options->add_field( array( 54 | 'name' => __( 'Test Color Picker', 'myprefix' ), 55 | 'desc' => __( 'field description (optional)', 'myprefix' ), 56 | 'id' => 'test_colorpicker', 57 | 'type' => 'colorpicker', 58 | 'default' => '#bada55', 59 | ) ); 60 | 61 | 62 | } 63 | 64 | /** 65 | * Wrapper function around cmb2_get_option 66 | * @since 0.1.0 67 | * @param string $key Options array key 68 | * @param mixed $default Optional default value 69 | * @return mixed Option value 70 | */ 71 | function myprefix_get_network_option( $key = '', $default = false ) { 72 | if ( function_exists( 'cmb2_get_option' ) ) { 73 | // Use cmb2_get_option as it passes through some key filters. 74 | return cmb2_get_option( 'myprefix_network_options', $key, $default ); 75 | } 76 | 77 | // Fallback to get_site_option if CMB2 is not loaded yet. 78 | $opts = get_site_option( 'myprefix_network_options', $default ); 79 | 80 | $val = $default; 81 | 82 | if ( 'all' == $key ) { 83 | $val = $opts; 84 | } elseif ( is_array( $opts ) && array_key_exists( $key, $opts ) && false !== $opts[ $key ] ) { 85 | $val = $opts[ $key ]; 86 | } 87 | 88 | return $val; 89 | } 90 | -------------------------------------------------------------------------------- /options-and-settings-pages/non-cmb2-options-page.php: -------------------------------------------------------------------------------- 1 | 'wporg_field_pill', 35 | 'class' => 'wporg_row', 36 | 'wporg_custom_data' => 'custom', 37 | ) 38 | ); 39 | } 40 | 41 | /** 42 | * Register our wporg_settings_init to the admin_init action hook. 43 | */ 44 | add_action( 'admin_init', 'wporg_settings_init' ); 45 | 46 | 47 | /** 48 | * Custom option and settings: 49 | * - callback functions 50 | */ 51 | 52 | 53 | /** 54 | * Developers section callback function. 55 | * 56 | * @param array $args The settings array, defining title, id, callback. 57 | */ 58 | function wporg_section_developers_callback( $args ) { 59 | ?> 60 |

    61 | . 69 | * - the "class" key value is used for the "class" attribute of the containing the field. 70 | * Note: you can add custom key value pairs to be used inside your callbacks. 71 | * 72 | * @param array $args 73 | */ 74 | function wporg_field_pill_cb( $args ) { 75 | // Get the value of the setting we've registered with register_setting() 76 | $options = get_option( 'wporg_options' ); 77 | ?> 78 | 90 |

    91 | 92 |

    93 |

    94 | 95 |

    96 | 144 |
    145 |

    146 | options_page_tab_nav_output(); ?> 147 |
    148 | 157 |
    158 |
    159 | 'yourprefix_main_options_page', 13 | 'title' => esc_html__( 'Main Options', 'cmb2' ), 14 | 'object_types' => array( 'options-page' ), 15 | 16 | /* 17 | * The following parameters are specific to the options-page box 18 | * Several of these parameters are passed along to add_menu_page()/add_submenu_page(). 19 | */ 20 | 21 | 'option_key' => 'yourprefix_main_options', // The option key and admin menu page slug. 22 | // 'icon_url' => 'dashicons-palmtree', // Menu icon. Only applicable if 'parent_slug' is left empty. 23 | // 'menu_title' => esc_html__( 'Options', 'cmb2' ), // Falls back to 'title' (above). 24 | // 'parent_slug' => 'themes.php', // Make options page a submenu item of the themes menu. 25 | // 'capability' => 'manage_options', // Cap required to view options-page. 26 | // 'position' => 1, // Menu position. Only applicable if 'parent_slug' is left empty. 27 | // 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page. 28 | // 'display_cb' => false, // Override the options-page form output (CMB2_Hookup::options_page_output()). 29 | // 'save_button' => esc_html__( 'Save Theme Options', 'cmb2' ), // The text for the options-page save button. Defaults to 'Save'. 30 | // 'disable_settings_errors' => true, // On settings pages (not options-general.php sub-pages), allows disabling. 31 | // 'message_cb' => 'yourprefix_options_page_message_callback', 32 | ) ); 33 | 34 | /** 35 | * Options fields ids only need 36 | * to be unique within this box. 37 | * Prefix is not needed. 38 | */ 39 | $main_options->add_field( array( 40 | 'name' => esc_html__( 'Site Background Color', 'cmb2' ), 41 | 'desc' => esc_html__( 'field description (optional)', 'cmb2' ), 42 | 'id' => 'bg_color', 43 | 'type' => 'colorpicker', 44 | 'default' => '#ffffff', 45 | ) ); 46 | 47 | /** 48 | * Registers secondary options page, and set main item as parent. 49 | */ 50 | $secondary_options = new_cmb2_box( array( 51 | 'id' => 'yourprefix_secondary_options_page', 52 | 'title' => esc_html__( 'Secondary Options', 'cmb2' ), 53 | 'object_types' => array( 'options-page' ), 54 | 'option_key' => 'yourprefix_secondary_options', 55 | 'parent_slug' => 'yourprefix_main_options', 56 | ) ); 57 | 58 | $secondary_options->add_field( array( 59 | 'name' => esc_html__( 'Test Radio', 'cmb2' ), 60 | 'desc' => esc_html__( 'field description (optional)', 'cmb2' ), 61 | 'id' => 'radio', 62 | 'type' => 'radio', 63 | 'options' => array( 64 | 'option1' => esc_html__( 'Option One', 'cmb2' ), 65 | 'option2' => esc_html__( 'Option Two', 'cmb2' ), 66 | 'option3' => esc_html__( 'Option Three', 'cmb2' ), 67 | ), 68 | ) ); 69 | 70 | /** 71 | * Registers tertiary options page, and set main item as parent. 72 | */ 73 | $tertiary_options = new_cmb2_box( array( 74 | 'id' => 'yourprefix_tertiary_options_page', 75 | 'title' => esc_html__( 'Tertiary Options', 'cmb2' ), 76 | 'object_types' => array( 'options-page' ), 77 | 'option_key' => 'yourprefix_tertiary_options', 78 | 'parent_slug' => 'yourprefix_main_options', 79 | ) ); 80 | 81 | $tertiary_options->add_field( array( 82 | 'name' => esc_html__( 'Test Text Area for Code', 'cmb2' ), 83 | 'desc' => esc_html__( 'field description (optional)', 'cmb2' ), 84 | 'id' => 'textarea_code', 85 | 'type' => 'textarea_code', 86 | ) ); 87 | 88 | } 89 | add_action( 'cmb2_admin_init', 'yourprefix_register_main_options_metabox' ); 90 | -------------------------------------------------------------------------------- /options-and-settings-pages/options-pages-with-tabs-and-submenus.php: -------------------------------------------------------------------------------- 1 | 'yourprefix_main_options_page', 12 | 'title' => 'Main Options', 13 | 'object_types' => array( 'options-page' ), 14 | 'option_key' => 'yourprefix_main_options', 15 | 'tab_group' => 'yourprefix_main_options', 16 | 'tab_title' => 'Main', 17 | ); 18 | 19 | // 'tab_group' property is supported in > 2.4.0. 20 | if ( version_compare( CMB2_VERSION, '2.4.0' ) ) { 21 | $args['display_cb'] = 'yourprefix_options_display_with_tabs'; 22 | } 23 | 24 | $main_options = new_cmb2_box( $args ); 25 | 26 | /** 27 | * Options fields ids only need 28 | * to be unique within this box. 29 | * Prefix is not needed. 30 | */ 31 | $main_options->add_field( array( 32 | 'name' => 'Site Background Color', 33 | 'desc' => 'field description (optional)', 34 | 'id' => 'bg_color', 35 | 'type' => 'colorpicker', 36 | 'default' => '#ffffff', 37 | ) ); 38 | 39 | /** 40 | * Registers secondary options page, and set main item as parent. 41 | */ 42 | $args = array( 43 | 'id' => 'yourprefix_secondary_options_page', 44 | 'menu_title' => 'Secondary Options', // Use menu title, & not title to hide main h2. 45 | 'object_types' => array( 'options-page' ), 46 | 'option_key' => 'yourprefix_secondary_options', 47 | 'parent_slug' => 'yourprefix_main_options', 48 | 'tab_group' => 'yourprefix_main_options', 49 | 'tab_title' => 'Secondary', 50 | ); 51 | 52 | // 'tab_group' property is supported in > 2.4.0. 53 | if ( version_compare( CMB2_VERSION, '2.4.0' ) ) { 54 | $args['display_cb'] = 'yourprefix_options_display_with_tabs'; 55 | } 56 | 57 | $secondary_options = new_cmb2_box( $args ); 58 | 59 | $secondary_options->add_field( array( 60 | 'name' => 'Test Radio', 61 | 'desc' => 'field description (optional)', 62 | 'id' => 'radio', 63 | 'type' => 'radio', 64 | 'options' => array( 65 | 'option1' => 'Option One', 66 | 'option2' => 'Option Two', 67 | 'option3' => 'Option Three', 68 | ), 69 | ) ); 70 | 71 | /** 72 | * Registers tertiary options page, and set main item as parent. 73 | */ 74 | $args = array( 75 | 'id' => 'yourprefix_tertiary_options_page', 76 | 'menu_title' => 'Tertiary Options', // Use menu title, & not title to hide main h2. 77 | 'object_types' => array( 'options-page' ), 78 | 'option_key' => 'yourprefix_tertiary_options', 79 | 'parent_slug' => 'yourprefix_main_options', 80 | 'tab_group' => 'yourprefix_main_options', 81 | 'tab_title' => 'Tertiary', 82 | ); 83 | 84 | // 'tab_group' property is supported in > 2.4.0. 85 | if ( version_compare( CMB2_VERSION, '2.4.0' ) ) { 86 | $args['display_cb'] = 'yourprefix_options_display_with_tabs'; 87 | } 88 | 89 | $tertiary_options = new_cmb2_box( $args ); 90 | 91 | $tertiary_options->add_field( array( 92 | 'name' => 'Test Text Area for Code', 93 | 'desc' => 'field description (optional)', 94 | 'id' => 'textarea_code', 95 | 'type' => 'textarea_code', 96 | ) ); 97 | 98 | } 99 | add_action( 'cmb2_admin_init', 'yourprefix_register_main_options_metabox' ); 100 | 101 | /** 102 | * A CMB2 options-page display callback override which adds tab navigation among 103 | * CMB2 options pages which share this same display callback. 104 | * 105 | * @param CMB2_Options_Hookup $cmb_options The CMB2_Options_Hookup object. 106 | */ 107 | function yourprefix_options_display_with_tabs( $cmb_options ) { 108 | $tabs = yourprefix_options_page_tabs( $cmb_options ); 109 | ?> 110 |
    111 | 112 |

    113 | 114 | 119 |
    120 | 121 | options_page_metabox(); ?> 122 | cmb->prop( 'save_button' ) ), 'primary', 'submit-cmb' ); ?> 123 |
    124 |
    125 | cmb->prop( 'tab_group' ); 138 | $tabs = array(); 139 | 140 | foreach ( CMB2_Boxes::get_all() as $cmb_id => $cmb ) { 141 | if ( $tab_group === $cmb->prop( 'tab_group' ) ) { 142 | $tabs[ $cmb->options_page_keys()[0] ] = $cmb->prop( 'tab_title' ) 143 | ? $cmb->prop( 'tab_title' ) 144 | : $cmb->prop( 'title' ); 145 | } 146 | } 147 | 148 | return $tabs; 149 | } 150 | -------------------------------------------------------------------------------- /options-and-settings-pages/submenu-options-pages.php: -------------------------------------------------------------------------------- 1 | 'yourprefix_options_submenu_page', 13 | 'title' => esc_html__( 'Page Options', 'cmb2' ), 14 | 'object_types' => array( 'options-page' ), 15 | 16 | /* 17 | * The following parameters are specific to the options-page box 18 | * Several of these parameters are passed along to add_menu_page()/add_submenu_page(). 19 | */ 20 | 21 | 'option_key' => 'yourprefix_page_options', // The option key and admin menu page slug. 22 | // 'icon_url' => '', // Menu icon. Only applicable if 'parent_slug' is left empty. 23 | // 'menu_title' => esc_html__( 'Options', 'cmb2' ), // Falls back to 'title' (above). 24 | 'parent_slug' => 'edit.php?post_type=page', // Make options page a submenu item of the themes menu. 25 | // 'capability' => 'manage_options', // Cap required to view options-page. 26 | // 'position' => 1, // Menu position. Only applicable if 'parent_slug' is left empty. 27 | // 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page. 28 | // 'display_cb' => false, // Override the options-page form output (CMB2_Hookup::options_page_output()). 29 | // 'save_button' => esc_html__( 'Save Theme Options', 'cmb2' ), // The text for the options-page save button. Defaults to 'Save'. 30 | // 'disable_settings_errors' => true, // On settings pages (not options-general.php sub-pages), allows disabling. 31 | // 'message_cb' => 'yourprefix_options_page_message_callback', 32 | ) ); 33 | 34 | $cmb->add_field( array( 35 | 'name' => esc_html__( 'Background Color for Pages', 'cmb2' ), 36 | 'desc' => esc_html__( 'field description (optional)', 'cmb2' ), 37 | 'id' => 'bg_color', 38 | 'type' => 'colorpicker', 39 | 'default' => '#ffffff', 40 | ) ); 41 | 42 | } 43 | add_action( 'cmb2_admin_init', 'yourprefix_register_options_submenu_for_page_post_type' ); 44 | 45 | /** 46 | * Hook in and register a submenu options page for the Appearance menu. 47 | */ 48 | function yourprefix_register_options_submenu_appearance_menu() { 49 | 50 | /** 51 | * Registers options page menu item and form. 52 | */ 53 | $cmb_options = new_cmb2_box( array( 54 | 'id' => 'yourprefix_options_submenu_appearance_menu', 55 | 'title' => esc_html__( 'Appearance Options', 'cmb2' ), 56 | 'object_types' => array( 'options-page' ), 57 | 58 | /* 59 | * The following parameters are specific to the options-page box 60 | * Several of these parameters are passed along to add_menu_page()/add_submenu_page(). 61 | */ 62 | 63 | 'option_key' => 'yourprefix_theme_appearance_options', // The option key and admin menu page slug. 64 | // 'icon_url' => '', // Menu icon. Only applicable if 'parent_slug' is left empty. 65 | // 'menu_title' => esc_html__( 'Options', 'cmb2' ), // Falls back to 'title' (above). 66 | 'parent_slug' => 'themes.php', // Make options page a submenu item of the themes menu. 67 | // 'capability' => 'manage_options', // Cap required to view options-page. 68 | // 'position' => 1, // Menu position. Only applicable if 'parent_slug' is left empty. 69 | // 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page. 70 | // 'display_cb' => false, // Override the options-page form output (CMB2_Hookup::options_page_output()). 71 | // 'save_button' => esc_html__( 'Save Theme Options', 'cmb2' ), // The text for the options-page save button. Defaults to 'Save'. 72 | // 'disable_settings_errors' => true, // On settings pages (not options-general.php sub-pages), allows disabling. 73 | // 'message_cb' => 'yourprefix_options_page_message_callback', 74 | ) ); 75 | 76 | /** 77 | * Options fields ids only need 78 | * to be unique within this box. 79 | * Prefix is not needed. 80 | */ 81 | $cmb_options->add_field( array( 82 | 'name' => esc_html__( 'Site Background Color', 'cmb2' ), 83 | 'desc' => esc_html__( 'field description (optional)', 'cmb2' ), 84 | 'id' => 'bg_color', 85 | 'type' => 'colorpicker', 86 | 'default' => '#ffffff', 87 | ) ); 88 | 89 | } 90 | add_action( 'cmb2_admin_init', 'yourprefix_register_options_submenu_appearance_menu' ); 91 | -------------------------------------------------------------------------------- /options-and-settings-pages/theme-options-cmb.php: -------------------------------------------------------------------------------- 1 | 'myprefix_option_metabox', 20 | 'title' => esc_html__( 'Site Options', 'myprefix' ), 21 | 'object_types' => array( 'options-page' ), 22 | 23 | /* 24 | * The following parameters are specific to the options-page box 25 | * Several of these parameters are passed along to add_menu_page()/add_submenu_page(). 26 | */ 27 | 28 | 'option_key' => 'myprefix_options', // The option key and admin menu page slug. 29 | // 'icon_url' => 'dashicons-palmtree', // Menu icon. Only applicable if 'parent_slug' is left empty. 30 | // 'menu_title' => esc_html__( 'Options', 'myprefix' ), // Falls back to 'title' (above). 31 | // 'parent_slug' => 'themes.php', // Make options page a submenu item of the themes menu. 32 | // 'capability' => 'manage_options', // Cap required to view options-page. 33 | // 'position' => 1, // Menu position. Only applicable if 'parent_slug' is left empty. 34 | // 'admin_menu_hook' => 'network_admin_menu', // 'network_admin_menu' to add network-level options page. 35 | // 'display_cb' => false, // Override the options-page form output (CMB2_Hookup::options_page_output()). 36 | // 'save_button' => esc_html__( 'Save Theme Options', 'myprefix' ), // The text for the options-page save button. Defaults to 'Save'. 37 | ) ); 38 | 39 | /* 40 | * Options fields ids only need 41 | * to be unique within this box. 42 | * Prefix is not needed. 43 | */ 44 | 45 | $cmb_options->add_field( array( 46 | 'name' => __( 'Test Text', 'myprefix' ), 47 | 'desc' => __( 'field description (optional)', 'myprefix' ), 48 | 'id' => 'test_text', 49 | 'type' => 'text', 50 | 'default' => 'Default Text', 51 | ) ); 52 | 53 | $cmb_options->add_field( array( 54 | 'name' => __( 'Test Color Picker', 'myprefix' ), 55 | 'desc' => __( 'field description (optional)', 'myprefix' ), 56 | 'id' => 'test_colorpicker', 57 | 'type' => 'colorpicker', 58 | 'default' => '#bada55', 59 | ) ); 60 | 61 | } 62 | 63 | /** 64 | * Wrapper function around cmb2_get_option 65 | * @since 0.1.0 66 | * @param string $key Options array key 67 | * @param mixed $default Optional default value 68 | * @return mixed Option value 69 | */ 70 | function myprefix_get_option( $key = '', $default = false ) { 71 | if ( function_exists( 'cmb2_get_option' ) ) { 72 | // Use cmb2_get_option as it passes through some key filters. 73 | return cmb2_get_option( 'myprefix_options', $key, $default ); 74 | } 75 | 76 | // Fallback to get_option if CMB2 is not loaded yet. 77 | $opts = get_option( 'myprefix_options', $default ); 78 | 79 | $val = $default; 80 | 81 | if ( 'all' == $key ) { 82 | $val = $opts; 83 | } elseif ( is_array( $opts ) && array_key_exists( $key, $opts ) && false !== $opts[ $key ] ) { 84 | $val = $opts[ $key ]; 85 | } 86 | 87 | return $val; 88 | } 89 | -------------------------------------------------------------------------------- /user-meta-and-settings/README.md: -------------------------------------------------------------------------------- 1 | User Meta and Settings 2 | ========== 3 | 4 | These are examples of [using CMB2 to generate user fields](https://github.com/WebDevStudios/CMB2/wiki/Adding-metaboxes-to-user-profile). -------------------------------------------------------------------------------- /widgets/widget-example.php: -------------------------------------------------------------------------------- 1 | widget_slug, 69 | esc_html__( 'CMB2 Widget Boilerplate Title', 'your-textdomain' ), 70 | array( 71 | 'classname' => $this->widget_slug, 72 | 'customize_selective_refresh' => true, 73 | 'description' => esc_html__( 'A CMB2 widget boilerplate description.', 'your-textdomain' ), 74 | ) 75 | ); 76 | 77 | self::$defaults = array( 78 | 'title' => esc_html__( 'CMB2 Widget Title', 'your-textdomain' ), 79 | 'image' => '', 80 | 'desc' => '', 81 | 'color' => '#bada55', 82 | ); 83 | 84 | $this->cmb2_fields = array( 85 | array( 86 | 'name' => 'Title', 87 | 'id_key' => 'title', 88 | 'id' => 'title', 89 | 'type' => 'text', 90 | ), 91 | array( 92 | 'name' => 'Image', 93 | 'desc' => 'Upload an image or enter an URL.', 94 | 'id_key' => 'image', 95 | 'id' => 'image', 96 | 'type' => 'file', 97 | 'options' => array( 98 | 'url' => false 99 | ), 100 | 'text' => array( 101 | 'add_upload_file_text' => 'Upload An Image' 102 | ), 103 | ), 104 | array( 105 | 'name' => 'Description', 106 | 'id_key' => 'desc', 107 | 'id' => 'desc', 108 | 'type' => 'textarea', 109 | ), 110 | array( 111 | 'name' => 'Color', 112 | 'id_key' => 'color', 113 | 'id' => 'color', 114 | 'type' => 'colorpicker', 115 | ), 116 | ); 117 | 118 | add_action( 'save_post', array( $this, 'flush_widget_cache' ) ); 119 | add_action( 'deleted_post', array( $this, 'flush_widget_cache' ) ); 120 | add_action( 'switch_theme', array( $this, 'flush_widget_cache' ) ); 121 | add_shortcode( self::$shortcode, array( __CLASS__, 'get_widget' ) ); 122 | } 123 | 124 | /** 125 | * Delete this widget's cache. 126 | * 127 | * Note: Could also delete any transients 128 | * delete_transient( 'some-transient-generated-by-this-widget' ); 129 | */ 130 | public function flush_widget_cache() { 131 | wp_cache_delete( $this->id, 'widget' ); 132 | } 133 | 134 | /** 135 | * Front-end display of widget. 136 | * 137 | * @param array $args The widget arguments set up when a sidebar is registered. 138 | * @param array $instance The widget settings as set by user. 139 | */ 140 | public function widget( $args, $instance ) { 141 | 142 | echo self::get_widget( array( 143 | 'args' => $args, 144 | 'instance' => $instance, 145 | 'cache_id' => $this->id, // whatever the widget id is 146 | ) ); 147 | 148 | } 149 | 150 | /** 151 | * Return the widget/shortcode output 152 | * 153 | * @param array $atts Array of widget/shortcode attributes/args 154 | * @return string Widget output 155 | */ 156 | public static function get_widget( $atts ) { 157 | $widget = ''; 158 | 159 | // Set up default values for attributes 160 | $atts = shortcode_atts( 161 | array( 162 | // Ensure variables 163 | 'instance' => array(), 164 | 'before_widget' => '', 165 | 'after_widget' => '', 166 | 'before_title' => '', 167 | 'after_title' => '', 168 | 'cache_id' => '', 169 | 'flush_cache' => isset( $_GET['delete-trans'] ), // Check for cache-buster 170 | ), 171 | isset( $atts['args'] ) ? (array) $atts['args'] : array(), 172 | self::$shortcode 173 | ); 174 | 175 | $instance = shortcode_atts( 176 | self::$defaults, 177 | ! empty( $atts['instance'] ) ? (array) $atts['instance'] : array(), 178 | self::$shortcode 179 | ); 180 | 181 | /* 182 | * If cache_id is not passed, we're not using the widget (but the shortcode), 183 | * so generate a hash cache id from the shortcode arguments 184 | */ 185 | if ( empty( $atts['cache_id'] ) ) { 186 | $atts['cache_id'] = md5( serialize( $atts ) ); 187 | } 188 | 189 | // Get from cache unless being requested not to 190 | $widget = ! $atts['flush_cache'] 191 | ? wp_cache_get( $atts['cache_id'], 'widget' ) 192 | : ''; 193 | 194 | // If $widget is empty, rebuild our cache 195 | if ( empty( $widget ) ) { 196 | 197 | $widget = ''; 198 | 199 | // Before widget hook 200 | $widget .= $atts['before_widget']; 201 | 202 | $widget .= '
    '; 203 | 204 | // Title 205 | $widget .= ( $instance['title'] ) ? $atts['before_title'] . esc_html( $instance['title'] ) . $atts['after_title'] : ''; 206 | 207 | $widget .= wpautop( wp_kses_post( $instance['desc'] ) ); 208 | 209 | $widget .= '
    '; 210 | 211 | // After widget hook 212 | $widget .= $atts['after_widget']; 213 | 214 | wp_cache_set( $atts['cache_id'], $widget, 'widget', WEEK_IN_SECONDS ); 215 | 216 | } 217 | 218 | return $widget; 219 | } 220 | 221 | /** 222 | * Update form values as they are saved. 223 | * 224 | * @param array $new_instance New settings for this instance as input by the user. 225 | * @param array $old_instance Old settings for this instance. 226 | * @return array Settings to save or bool false to cancel saving. 227 | */ 228 | public function update( $new_instance, $old_instance ) { 229 | $this->flush_widget_cache(); 230 | $sanitized = $this->cmb2( true )->get_sanitized_values( $new_instance ); 231 | return $sanitized; 232 | } 233 | /** 234 | * Back-end widget form with defaults. 235 | * 236 | * @param array $instance Current settings. 237 | */ 238 | public function form( $instance ) { 239 | // If there are no settings, set up defaults 240 | $this->_instance = wp_parse_args( (array) $instance, self::$defaults ); 241 | 242 | $cmb2 = $this->cmb2(); 243 | 244 | $cmb2->object_id( $this->option_name ); 245 | CMB2_hookup::enqueue_cmb_css(); 246 | CMB2_hookup::enqueue_cmb_js(); 247 | $cmb2->show_form(); 248 | } 249 | 250 | /** 251 | * Creates a new instance of CMB2 and adds some fields 252 | * @since 0.1.0 253 | * @return CMB2 254 | */ 255 | public function cmb2( $saving = false ) { 256 | 257 | // Create a new box in the class 258 | $cmb2 = new CMB2( array( 259 | 'id' => $this->option_name .'_box', // Option name is taken from the WP_Widget class. 260 | 'hookup' => false, 261 | 'show_on' => array( 262 | 'key' => 'options-page', // Tells CMB2 to handle this as an option 263 | 'value' => array( $this->option_name ) 264 | ), 265 | ), $this->option_name ); 266 | 267 | foreach ( $this->cmb2_fields as $field ) { 268 | 269 | if ( ! $saving ) { 270 | $field['id'] = $this->get_field_name( $field['id'] ); 271 | } 272 | 273 | $field['default_cb'] = array( $this, 'default_cb' ); 274 | 275 | $cmb2->add_field( $field ); 276 | } 277 | 278 | return $cmb2; 279 | } 280 | 281 | /** 282 | * Sets the field default, or the field value. 283 | * 284 | * @param array $field_args CMB2 field args array 285 | * @param CMB2_Field $field CMB2 Field object. 286 | * 287 | * @return mixed Field value. 288 | */ 289 | public function default_cb( $field_args, $field ) { 290 | return isset( $this->_instance[ $field->args( 'id_key' ) ] ) 291 | ? $this->_instance[ $field->args( 'id_key' ) ] 292 | : null; 293 | } 294 | 295 | } 296 | 297 | /** 298 | * Register this widget with WordPress. 299 | */ 300 | function register_wds_widget_boilerplate() { 301 | register_widget( 'CMB2_Widget_Boilerplate' ); 302 | } 303 | add_action( 'widgets_init', 'register_wds_widget_boilerplate' ); 304 | --------------------------------------------------------------------------------