diff --git a/.travis.yml b/.travis.yml index ab1afeaf..716bb632 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,36 +1,61 @@ language: php php: - - 5.3 - 5.4 - 5.5 - 5.6 - 7.0 + - 7.1 - hhvm env: - WP_VERSION=latest WP_MULTISITE=0 - WP_VERSION=latest WP_MULTISITE=1 - - WP_VERSION=3.8 WP_MULTISITE=0 - - WP_VERSION=3.8 WP_MULTISITE=1 + - WP_VERSION=4.1 WP_MULTISITE=0 + - WP_VERSION=4.1 WP_MULTISITE=1 matrix: allow_failures: - php: hhvm fast_finish: true exclude: - # Only test latest version of WP with HHVM + # We really only need to verify that each PHP/WP combination works generally, not that multisite works between them + - php: 5.4 + env: WP_VERSION=4.1 WP_MULTISITE=1 + - php: 5.4 + env: WP_VERSION=latest WP_MULTISITE=1 + - php: 5.5 + env: WP_VERSION=4.1 WP_MULTISITE=1 + - php: 5.5 + env: WP_VERSION=latest WP_MULTISITE=1 + - php: 7.0 + env: WP_VERSION=latest WP_MULTISITE=1 + # Only test latest version of WP with HHVM - php: hhvm - env: WP_VERSION=3.8 WP_MULTISITE=0 + env: WP_VERSION=4.1 WP_MULTISITE=0 - php: hhvm - env: WP_VERSION=3.8 WP_MULTISITE=1 - # Only test latest version of WP with PHP 7.0 because 3.8 won't work at all + env: WP_VERSION=4.1 WP_MULTISITE=1 + # Only test latest version of WP with PHP 7.0+ because 4.1 won't work at all - php: 7.0 - env: WP_VERSION=3.8 WP_MULTISITE=0 + env: WP_VERSION=4.1 WP_MULTISITE=0 - php: 7.0 - env: WP_VERSION=3.8 WP_MULTISITE=1 + env: WP_VERSION=4.1 WP_MULTISITE=1 + - php: 7.1 + env: WP_VERSION=4.1 WP_MULTISITE=0 + - php: 7.1 + env: WP_VERSION=4.1 WP_MULTISITE=1 before_script: - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION + - export PATH="$HOME/.composer/vendor/bin:$PATH" + - | + if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then + composer global require "phpunit/phpunit=5.7.*" + elif [[ ${TRAVIS_PHP_VERSION:0:3} != "5.2" ]]; then + composer global require "phpunit/phpunit=4.8.*" + fi + - which phpunit + - phpunit --version -script: phpunit +script: + - phpunit diff --git a/class.cmb-meta-box.php b/class-cmb-meta-box.php similarity index 63% rename from class.cmb-meta-box.php rename to class-cmb-meta-box.php index e53d4cf8..6b7c33e7 100644 --- a/class.cmb-meta-box.php +++ b/class-cmb-meta-box.php @@ -2,15 +2,12 @@ /** * Base functionality for HM CMB plugin. * + * @since 1.0.0 + * * @package WordPress * @subpackage Custom Meta Boxes */ -/** - * Create Meta Boxes. - * - * @since 1.0.0 - */ class CMB_Meta_Box { /** @@ -29,7 +26,7 @@ class CMB_Meta_Box { * * @var array */ - private $fields = array(); + public $fields = array(); /** * CMB_Meta_Box constructor. @@ -55,7 +52,14 @@ function __construct( $meta_box ) { add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_scripts' ) ); add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_styles' ) ); - + add_action( 'wp_ajax_cmb_post_select', array( $this, 'cmb_ajax_post_select' ) ); + + // Default filters for whether to show a metabox block or not. + add_filter( 'cmb_is_metabox_displayed', array( $this, 'add_for_id' ), 2, 2 ); + add_filter( 'cmb_is_metabox_displayed', array( $this, 'hide_for_id' ), 3, 2 ); + add_filter( 'cmb_is_metabox_displayed', array( $this, 'add_for_page_template' ), 4, 2 ); + add_filter( 'cmb_is_metabox_displayed', array( $this, 'hide_for_page_template' ), 5, 2 ); + add_filter( 'cmb_is_metabox_displayed', array( $this, 'check_capabilities' ), 5, 2 ); } /** @@ -75,16 +79,34 @@ public function init_fields( $post_id = 0 ) { unset( $args['type'] ); unset( $args['name'] ); - $class = _cmb_field_class_for_type( $field['type'] ); + $class = _cmb_field_class_for_type( $field['type'] ); + $single = ( ! isset( $field['repeatable'] ) || false === $field['repeatable'] ); // If we are on a post edit screen - get metadata value of the field for this post. if ( $post_id ) { - $single = ( ! isset( $field['repeatable'] ) || false === $field['repeatable'] ); $values = (array) get_post_meta( $post_id, $field['id'], $single ); } + /** + * Filter which fields are consdered to be "group" types. + * + * This is useful if you want to extend the group field for your own use and still + * use the array mapping below. + * + * @param array $group_fields Group field types + */ + $group_field_types = apply_filters( 'cmb_group_field_types', array( 'group' ) ); + + // Handle repeatable values for group fields. + if ( in_array( $field['type'], $group_field_types ) && $single ) { + $values = array( $values ); + } + if ( class_exists( $class ) ) { - $this->fields[] = new $class( $field['id'], $field['name'], (array) $values, $args ); + $field = new $class( $field['id'], $field['name'], (array) $values, $args ); + if ( $field->is_displayed( $post_id ) ) { + $this->fields[] = $field; + } } } @@ -132,13 +154,16 @@ public function init_fields_for_post() { */ function enqueue_scripts() { - wp_enqueue_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); + if ( ! wp_script_is( 'cmb-scripts' ) ) { - wp_localize_script( 'cmb-scripts', 'CMBData', array( - 'strings' => array( - 'confirmDeleteField' => esc_html__( 'Are you sure you want to delete this field?', 'cmb' ), - ), - ) ); + wp_enqueue_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ), CMB_VERSION ); + + wp_localize_script( 'cmb-scripts', 'CMBData', array( + 'strings' => array( + 'confirmDeleteField' => esc_html__( 'Are you sure you want to delete this field?', 'cmb' ), + ), + ) ); + } foreach ( $this->fields as $field ) { $field->enqueue_scripts(); @@ -156,9 +181,9 @@ function enqueue_styles() { $suffix = CMB_DEV ? '' : '.min'; if ( version_compare( get_bloginfo( 'version' ), '3.8', '>=' ) ) { - wp_enqueue_style( 'cmb-styles', trailingslashit( CMB_URL ) . "css/dist/cmb$suffix.css" ); + wp_enqueue_style( 'cmb-styles', trailingslashit( CMB_URL ) . "css/dist/cmb$suffix.css", array(), CMB_VERSION ); } else { - wp_enqueue_style( 'cmb-styles', trailingslashit( CMB_URL ) . 'css/legacy.css' ); + wp_enqueue_style( 'cmb-styles', trailingslashit( CMB_URL ) . 'css/legacy.css', array(), CMB_VERSION ); } foreach ( $this->fields as $field ) { @@ -202,12 +227,14 @@ function add() { * a CMB field collection. */ function is_metabox_displayed() { - $display = true; - $display = $this->add_for_id( $display ); - $display = $this->hide_for_id( $display ); - $display = $this->add_for_page_template( $display ); - $display = $this->hide_for_page_template( $display ); - return $display; + + /** + * Filter whether a metabox should be displayed or not. + * + * @param bool $is_displayed Current status of display + * @param array $metabox Metabox information + */ + return apply_filters( 'cmb_is_metabox_displayed', true, $this->_meta_box ); } /** @@ -215,12 +242,17 @@ function is_metabox_displayed() { * * Only works for field collections that have the 'show_on' attribute of 'id'. * - * @param bool $display Current display status. + * @param bool $display Current display status. + * @param array $field Field arguments. * @return bool (Potentially) modified display status */ - function add_for_id( $display ) { + function add_for_id( $display, $field = array() ) { - if ( ! isset( $this->_meta_box['show_on']['id'] ) ) { + if ( empty( $field ) ) { + $field = $this->_meta_box; + } + + if ( ! isset( $field['show_on']['id'] ) ) { return $display; } @@ -232,9 +264,9 @@ function add_for_id( $display ) { } // If value isn't an array, turn it into one. - $this->_meta_box['show_on']['id'] = ! is_array( $this->_meta_box['show_on']['id'] ) ? array( $this->_meta_box['show_on']['id'] ) : $this->_meta_box['show_on']['id']; + $field['show_on']['id'] = ! is_array( $field['show_on']['id'] ) ? array( $field['show_on']['id'] ) : $field['show_on']['id']; - return in_array( $post_id, $this->_meta_box['show_on']['id'] ); + return in_array( $post_id, $field['show_on']['id'] ); } @@ -243,12 +275,17 @@ function add_for_id( $display ) { * * Only works for field collections that have the 'hide_on' attribute of 'id'. * - * @param bool $display Current display status. + * @param bool $display Current display status. + * @param array $field Field arguments. * @return bool (Potentially) modified display status */ - function hide_for_id( $display ) { + function hide_for_id( $display, $field = array() ) { + + if ( empty( $field ) ) { + $field = $this->_meta_box; + } - if ( ! isset( $this->_meta_box['hide_on']['id'] ) ) { + if ( ! isset( $field['hide_on']['id'] ) ) { return $display; } @@ -259,9 +296,9 @@ function hide_for_id( $display ) { } // If value isn't an array, turn it into one. - $this->_meta_box['hide_on']['id'] = ! is_array( $this->_meta_box['hide_on']['id'] ) ? array( $this->_meta_box['hide_on']['id'] ) : $this->_meta_box['hide_on']['id']; + $field['hide_on']['id'] = ! is_array( $field['hide_on']['id'] ) ? array( $field['hide_on']['id'] ) : $field['hide_on']['id']; - return ! in_array( $post_id, $this->_meta_box['hide_on']['id'] ); + return ! in_array( $post_id, $field['hide_on']['id'] ); } @@ -270,12 +307,17 @@ function hide_for_id( $display ) { * * Only works for field collections that have the 'show_on' attribute of 'page-template'. * - * @param bool $display Current display status. + * @param bool $display Current display status. + * @param array $field Field arguments. * @return bool (Potentially) modified display status */ - function add_for_page_template( $display ) { + function add_for_page_template( $display, $field = array() ) { - if ( ! isset( $this->_meta_box['show_on']['page-template'] ) ) { + if ( empty( $field ) ) { + $field = $this->_meta_box; + } + + if ( ! isset( $field['show_on']['page-template'] ) ) { return $display; } @@ -289,9 +331,9 @@ function add_for_page_template( $display ) { $current_template = get_post_meta( $post_id, '_wp_page_template', true ); // If value isn't an array, turn it into one. - $this->_meta_box['show_on']['page-template'] = ! is_array( $this->_meta_box['show_on']['page-template'] ) ? array( $this->_meta_box['show_on']['page-template'] ) : $this->_meta_box['show_on']['page-template']; + $field['show_on']['page-template'] = ! is_array( $field['show_on']['page-template'] ) ? array( $field['show_on']['page-template'] ) : $field['show_on']['page-template']; - return in_array( $current_template, $this->_meta_box['show_on']['page-template'] ); + return in_array( $current_template, $field['show_on']['page-template'] ); } @@ -300,12 +342,17 @@ function add_for_page_template( $display ) { * * Only works for field collections that have the 'hide_on' attribute of 'page-template'. * - * @param bool $display Current display status. + * @param bool $display Current display status. + * @param array $field Field arguments. * @return bool (Potentially) modified display status */ - function hide_for_page_template( $display ) { + function hide_for_page_template( $display, $field = array() ) { + + if ( empty( $field ) ) { + $field = $this->_meta_box; + } - if ( ! isset( $this->_meta_box['hide_on']['page-template'] ) ) { + if ( ! isset( $field['hide_on']['page-template'] ) ) { return $display; } @@ -320,9 +367,28 @@ function hide_for_page_template( $display ) { $current_template = get_post_meta( $post_id, '_wp_page_template', true ); // If value isn't an array, turn it into one. - $this->_meta_box['hide_on']['page-template'] = ! is_array( $this->_meta_box['hide_on']['page-template'] ) ? array( $this->_meta_box['hide_on']['page-template'] ) : $this->_meta_box['hide_on']['page-template']; + $field['hide_on']['page-template'] = ! is_array( $field['hide_on']['page-template'] ) ? array( $field['hide_on']['page-template'] ) : $field['hide_on']['page-template']; - return ! in_array( $current_template, $this->_meta_box['hide_on']['page-template'] ); + return ! in_array( $current_template, $field['hide_on']['page-template'] ); + + } + + /** + * Check capabilities of current user before displaying a CMB block. + * + * Only works for field collections that have the 'capability' attribute set. + * + * @param bool $display Current display status. + * @param array $field Field arguments. + * @return bool (Potentially) modified display status + */ + function check_capabilities( $display, $field = array() ) { + + if ( ! isset( $this->_meta_box['capability'] ) ) { + return $display; + } + + return current_user_can( $this->_meta_box['capability'] ); } @@ -369,7 +435,7 @@ static function layout_fields( array $fields ) { foreach ( $fields as $field ) : - if ( 0 == $current_colspan ) : ?> + if ( 0 == $current_colspan && ! $field instanceof CMB_Hidden_Field ) : ?>
@@ -383,8 +449,11 @@ static function layout_fields( array $fields ) { $classes[] = 'repeatable'; } - if ( ! empty( $field->args['sortable'] ) ) { + if ( ! empty( $field->args['sortable'] ) && ! empty( $field->args['repeatable'] ) ) { $classes[] = 'cmb-sortable'; + } elseif ( ! empty( $field->args['sortable'] ) && empty( $field->args['repeatable'] ) ) { + // Throw an error if calling the wrong combination of sortable and repeatable. + _doing_it_wrong( 'cmb_meta_boxes', __( 'Calling sortable on a non-repeatable field. A field cannot be sortable without being repeatable.', 'cmb' ), 4.7 ); } // Assign extra class for has label or has no label. @@ -403,6 +472,11 @@ static function layout_fields( array $fields ) { if ( isset( $field->args['repeatable_max'] ) ) { $attrs[] = sprintf( 'data-rep-max="%s"', intval( $field->args['repeatable_max'] ) ); } + + // Ask for confirmation before removing field. + if ( isset( $field->args['confirm_delete'] ) ) { + $attrs[] = sprintf( 'data-confirm-delete="%s"', $field->args['confirm_delete'] ? 'true' : 'false' ); + } ?>
@@ -415,7 +489,7 @@ static function layout_fields( array $fields ) {
- @@ -463,6 +537,11 @@ function save( $post_id = 0 ) { return $post_id; } + // Verify this meta box is for the right post type + if ( ! in_array( get_post_type( $post_id ), (array) $this->_meta_box['pages'], true ) ) { + return $post_id; + } + foreach ( $this->_meta_box['fields'] as $field ) { // Verify this meta box was shown on the page. @@ -530,4 +609,34 @@ function get_post_id() { return (int) $post_id; } + + /** + * AJAX callback for select fields. + */ + public function cmb_ajax_post_select() { + + $post_id = ! empty( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : false; + $nonce = ! empty( $_POST['nonce'] ) ? $_POST['nonce'] : false; + $args = ! empty( $_POST['query'] ) ? $_POST['query'] : array(); + + if ( ! $nonce || ! wp_verify_nonce( $nonce, 'cmb_select_field' ) || ! current_user_can( 'edit_post', $post_id ) ) { + echo json_encode( array( 'total' => 0, 'posts' => array() ) ); + exit; + } + + $args['fields'] = 'ids'; // Only need to retrieve post IDs. + + $query = new WP_Query( $args ); + + $json = array( 'total' => $query->found_posts, 'posts' => array() ); + + foreach ( $query->posts as $post_id ) { + array_push( $json['posts'], array( 'id' => $post_id, 'text' => html_entity_decode( get_the_title( $post_id ) ) ) ); + } + + echo json_encode( $json ); + + exit; + + } } diff --git a/classes.fields.php b/classes.fields.php deleted file mode 100644 index 556a0965..00000000 --- a/classes.fields.php +++ /dev/null @@ -1,2175 +0,0 @@ -id = $name; - $this->name = $name . '[]'; - $this->title = $title; - $this->args = wp_parse_args( $args, $this->get_default_args() ); - - // Deprecated argument: 'std' - if ( ! empty( $this->args['std'] ) && empty( $this->args['default'] ) ) { - $this->args['default'] = $this->args['std']; - _deprecated_argument( 'CMB_Field', '0.9', "field argument 'std' is deprecated, use 'default' instead" ); - } - - if ( ! empty( $this->args['options'] ) && is_array( reset( $this->args['options'] ) ) ) { - $re_format = array(); - foreach ( $this->args['options'] as $option ) { - $re_format[ $option['value'] ] = $option['name']; - } - $this->args['options'] = $re_format; - } - - // If the field has a custom value populator callback. - if ( ! empty( $args['values_callback'] ) ) { - $this->values = call_user_func( $args['values_callback'], get_the_id() ); - } else { - $this->values = $values; - } - - $this->value = reset( $this->values ); - - } - - /** - * Get the default args for the abstract field. - * These args are available to all fields. - * - * @return array $args - */ - public function get_default_args() { - return apply_filters( - 'cmb_field_default_args', - array( - 'desc' => '', - 'repeatable' => false, - 'sortable' => false, - 'repeatable_max' => null, - 'show_label' => false, - 'readonly' => false, - 'disabled' => false, - 'default' => '', - 'cols' => '12', - 'style' => '', - 'class' => '', - 'data_delegate' => null, - 'save_callback' => null, - 'string-repeat-field' => __( 'Add New', 'cmb' ), - 'string-delete-field' => __( 'Remove', 'cmb' ), - ), - get_class( $this ) - ); - } - - /** - * Enqueue all scripts required by the field. - * - * @uses wp_enqueue_script() - */ - public function enqueue_scripts() { - - if ( isset( $this->args['sortable'] ) && $this->args['sortable'] ) { - wp_enqueue_script( 'jquery-ui-sortable' ); - } - - } - - /** - * Enqueue all styles required by the field. - * - * @uses wp_enqueue_style() - */ - public function enqueue_styles() {} - - /** - * Output the field input ID attribute. - * - * If multiple inputs are required for a single field, - * use the append parameter to add unique identifier. - * - * @param string $append Optional. ID to place. - */ - public function id_attr( $append = null ) { - - printf( 'id="%s"', esc_attr( $this->get_the_id_attr( $append ) ) ); - - } - - /** - * Output the for attribute for the field. - * - * If multiple inputs are required for a single field, - * use the append parameter to add unique identifier. - * - * @param string $append Optional. ID to place. - * @return string Modified id attribute contents - */ - public function get_the_id_attr( $append = null ) { - - $id = $this->id; - - if ( isset( $this->parent ) ) { - $parent_id = preg_replace( '/cmb\-field\-(\d+|x)/', 'cmb-group-$1', $this->parent->get_the_id_attr() ); - $id = $parent_id . '[' . $id . ']'; - } - - $id .= '-cmb-field-' . $this->field_index; - - if ( ! is_null( $append ) ) { - $id .= '-' . $append; - } - - $id = str_replace( array( '[', ']', '--' ), '-', $id ); - - return $id; - - } - - /** - * Output the field input ID attribute value. - * - * If multiple inputs are required for a single field, - * use the append parameter to add unique identifier. - * - * @see get_the_id_attr - * - * @param string $append Optional. for value to place. - */ - public function for_attr( $append = null ) { - - printf( 'for="%s"', esc_attr( $this->get_the_id_attr( $append ) ) ); - - } - - /** - * Output HTML name attribute for a field. - * - * @see get_the_name_attr - * - * @param string $append Optional. Name to place. - */ - public function name_attr( $append = null ) { - - printf( 'name="%s"', esc_attr( $this->get_the_name_attr( $append ) ) ); - - } - - /** - * Get the name attribute contents for a field. - * - * @param null $append Optional. Name to place. - * @return string Name attribute contents. - */ - public function get_the_name_attr( $append = null ) { - - $name = str_replace( '[]', '', $this->name ); - - if ( isset( $this->parent ) ) { - $parent_name = preg_replace( '/cmb\-field\-(\d+|x)/', 'cmb-group-$1', $this->parent->get_the_name_attr() ); - $name = $parent_name . '[' . $name . ']'; - } - - $name .= "[cmb-field-$this->field_index]"; - - if ( ! is_null( $append ) ) { - $name .= $append; - } - - return $name; - - } - - /** - * Output class attribute for a field. - * - * @param string $classes Optional. Classes to assign to the field. - */ - public function class_attr( $classes = '' ) { - - if ( $classes = implode( ' ', array_map( 'sanitize_html_class', array_filter( array_unique( explode( ' ', $classes . ' ' . $this->args['class'] ) ) ) ) ) ) { ?> - - class="" - - get_the_id_attr() ); - - } - - /** - * Print one or more HTML5 attributes for a field. - * - * @param array $attrs Optional. Attributes to define in the field. - */ - public function boolean_attr( $attrs = array() ) { - - if ( $this->args['readonly'] ) { - $attrs[] = 'readonly'; - } - - if ( $this->args['disabled'] ) { - $attrs[] = 'disabled'; - } - - $attrs = array_filter( array_unique( $attrs ) ); - - foreach ( $attrs as $attr ) { - echo esc_html( $attr ) . '="' . esc_attr( $attr ) . '"'; - } - - } - - /** - * Check if this field has a data delegate set - * - * @return boolean Set or turned off. - */ - public function has_data_delegate() { - return (bool) $this->args['data_delegate']; - } - - /** - * Get the array of data from the data delegate. - * - * @return array mixed - */ - protected function get_delegate_data() { - - if ( $this->args['data_delegate'] ) { - return call_user_func_array( $this->args['data_delegate'], array( $this ) ); - } - - return array(); - - } - - /** - * Get the existing or default value for a field. - * - * @return mixed - */ - public function get_value() { - return ( $this->value || '0' === $this->value ) ? $this->value : $this->args['default']; - } - - /** - * Get multiple values for a field. - * - * @return array - */ - public function &get_values() { - return $this->values; - } - - /** - * Define multiple values for a field and completely remove the singular value variable. - * - * @param array $values Field values. - */ - public function set_values( array $values ) { - - $this->values = $values; - - unset( $this->value ); - - } - - /** - * Parse and validate an array of values. - * - * Meant to be extended. - */ - public function parse_save_values() {} - - /** - * Parse and validate a single value. - * - * Meant to be extended. - */ - public function parse_save_value() {} - - /** - * Save values for the field. - * - * @todo this surely only works for posts - * @todo why do values need to be passed in, they can already be passed in on construct - * - * @param int $post_id Post ID. - * @param array $values Values to save. - */ - public function save( $post_id, $values ) { - - // Don't save readonly values. - if ( $this->args['readonly'] ) { - return; - } - - $this->values = $values; - $this->parse_save_values(); - - // Allow override from args. - if ( ! empty( $this->args['save_callback'] ) ) { - - call_user_func( $this->args['save_callback'], $this->values, $post_id ); - - return; - - } - - // If we are not on a post edit screen. - if ( ! $post_id ) { - return; - } - - delete_post_meta( $post_id, $this->id ); - - foreach ( $this->values as $v ) { - - $this->value = $v; - $this->parse_save_value(); - - if ( $this->value || '0' === $this->value ) { - add_post_meta( $post_id, $this->id, $this->value ); - } - } - } - - /** - * Print title for field. - */ - public function title() { - - if ( $this->title ) : ?> - -
- -
- - args['desc'] ) ) : ?> - -
- args['desc'] ); ?> -
- - get_values() && ! $this->args['repeatable'] ) { - $values = array( '' ); - } else { - $values = $this->get_values(); - } - - // Print title if necessary. - $this->title(); - - // Print description if necessary. - $this->description(); - - $i = 0; - if ( isset( $this->args['type'] ) && 'gmap' == $this->args['type'] ) { - $values = array( $values ); - } - - foreach ( $values as $key => $value ) { - - $this->field_index = $i; - $this->value = $value; ?> - -
- - args['repeatable'] ) : ?> - - - - html(); ?> - -
- - args['repeatable'] ) { - - // X used to distinguish hidden fields. - $this->field_index = 'x'; - $this->value = ''; ?> - - - - - - - - id_attr(); ?> boolean_attr(); ?> class_attr(); ?> name_attr(); ?> value="get_value() ); ?>" /> - - args['class'] .= ' cmb_text_small'; - - parent::html(); - - } -} - -/** - * Field for image upload / file upload. - * - * @todo ability to set image size (preview image) from caller - * - * @since 1.0.0 - * - * @extends CMB_Field - */ -class CMB_File_Field extends CMB_Field { - - /** - * Get default arguments for field including custom parameters. - * - * @return array Default arguments for field. - */ - public function get_default_args() { - return array_merge( - parent::get_default_args(), - array( - 'library-type' => array( - 'video', - 'audio', - 'text', - 'application', - ), - ) - ); - } - - /** - * Enqueue all scripts required by the field. - * - * @uses wp_enqueue_script() - */ - function enqueue_scripts() { - - global $post_ID; - $post_ID = isset( $post_ID ) ? (int) $post_ID : 0; - - parent::enqueue_scripts(); - - wp_enqueue_media( array( 'post' => $post_ID ) ); - wp_enqueue_script( 'cmb-file-upload', trailingslashit( CMB_URL ) . 'js/file-upload.js', array( 'jquery', 'cmb-scripts' ) ); - - } - - /** - * Print out field HTML. - */ - public function html() { - - if ( $this->get_value() ) { - $src = wp_mime_type_icon( $this->get_value() ); - $size = getimagesize( str_replace( site_url(), ABSPATH, $src ) ); - $icon_img = ''; - } - - $data_type = ( ! empty( $this->args['library-type'] ) ? implode( ',', $this->args['library-type'] ) : null ); - - ?> - -
> - -
- - - -
- - get_value() ) : ?> - - - -
- get_value() ) ) ); ?> -
- - - -
- - - - class_attr( 'cmb-file-upload-input' ); ?> - name_attr(); ?> - value="value ); ?>" - /> - -
- - 'thumbnail', - 'library-type' => array( - 'image', - ), - 'show_size' => false, - ) - ); - } - - /** - * Print out field HTML. - */ - public function html() { - - if ( $this->get_value() ) { - $image = wp_get_attachment_image_src( $this->get_value(), $this->args['size'], true ); - } - - // Convert size arg to array of width, height, crop. - $size = $this->parse_image_size( $this->args['size'] ); - - // Inline styles. - $styles = sprintf( 'width: %1$dpx; height: %2$dpx; line-height: %2$dpx', intval( $size['width'] ), intval( $size['height'] ) ); - $placeholder_styles = sprintf( 'width: %dpx; height: %dpx;', intval( $size['width'] ) - 8, intval( $size['height'] ) - 8 ); - - $data_type = ( ! empty( $this->args['library-type'] ) ? implode( ',', $this->args['library-type'] ) : null ); - - ?> - -
- -
- - args['show_size'] ) : ?> - - - - - -
- - - -
- - - - - -
- - - - class_attr( 'cmb-file-upload-input' ); ?> - name_attr(); ?> - value="value ); ?>" - /> - -
- - get_option( $size . '_size_w' ), - 'height' => get_option( $size . '_size_h' ), - 'crop' => get_option( $size . '_crop' ), - ); - } - - // Handle string for additional image sizes. - global $_wp_additional_image_sizes; - if ( is_string( $size ) && isset( $_wp_additional_image_sizes[ $size ] ) ) { - return array( - 'width' => $_wp_additional_image_sizes[ $size ]['width'], - 'height' => $_wp_additional_image_sizes[ $size ]['height'], - 'crop' => $_wp_additional_image_sizes[ $size ]['crop'], - ); - } - - // Handle default WP size format. - if ( is_array( $size ) && isset( $size[0] ) && isset( $size[1] ) ) { - $size = array( 'width' => $size[0], 'height' => $size[1] ); - } - - return wp_parse_args( $size, array( - 'width' => get_option( 'thumbnail_size_w' ), - 'height' => get_option( 'thumbnail_size_h' ), - 'crop' => get_option( 'thumbnail_crop' ), - ) ); - - } - - /** - * Ajax callback for outputing an image src based on post data. - * - * @return null - */ - static function request_image_ajax_callback() { - - if ( ! ( isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], 'cmb-file-upload-nonce' ) ) ) { - return; - } - - $id = absint( $_POST['id'] ); - - $size = array( - intval( $_POST['width'] ), - intval( $_POST['height'] ), - 'crop' => (bool) $_POST['crop'], - ); - - $image = wp_get_attachment_image_src( $id, $size ); - echo esc_url( reset( $image ) ); - - // This is required to return a proper result. - die(); - } -} -add_action( 'wp_ajax_cmb_request_image', array( 'CMB_Image_Field', 'request_image_ajax_callback' ) ); - -/** - * Number meta box. - * - * @since 1.0.0 - * - * @extends CMB_Field - */ -class CMB_Number_Field extends CMB_Field { - - /** - * Get default arguments for field including custom parameters. - * - * @return array Default arguments for field. - */ - public function get_default_args() { - return array_merge( - parent::get_default_args(), - array( - 'step' => '', - ) - ); - } - - /** - * Print out field HTML. - */ - public function html() { - ?> - - id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_text_number code' ); ?> name_attr(); ?> value="get_value() ); ?>" /> - - - - id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_text_url code' ); ?> name_attr(); ?> value="value ) ); ?>" /> - - args['cols'] ) && $this->args['cols'] <= 6 ) ? 'cmb_datepicker' : 'cmb_text_small cmb_datepicker' ; - ?> - - id_attr(); ?> boolean_attr(); ?> class_attr( $classes ); ?> type="text" name_attr(); ?> value="value ); ?>" /> - - - - id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_text_small cmb_timepicker' ); ?> type="text" name_attr(); ?> value="value ); ?>"/> - - - - id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_text_small cmb_datepicker' ); ?> type="text" name_attr(); ?> value="value ? esc_attr( date( 'm\/d\/Y', $this->value ) ) : '' ?>" /> - - values as &$value ) { - $value = strtotime( $value ); - } - - sort( $this->values ); - - } -} - -/** - * Date picker for date and time (seperate fields) box. - * - * @since 1.0.0 - * - * @extends CMB_Field - */ -class CMB_Datetime_Timestamp_Field extends CMB_Field { - - /** - * Enqueue all scripts required by the field. - * - * @uses wp_enqueue_script() - */ - public function enqueue_scripts() { - - parent::enqueue_scripts(); - - wp_enqueue_style( 'cmb-jquery-ui', trailingslashit( CMB_URL ) . 'css/vendor/jquery-ui/jquery-ui.css', '1.10.3' ); - - wp_enqueue_script( 'cmb-timepicker', trailingslashit( CMB_URL ) . 'js/jquery.timePicker.min.js', array( 'jquery', 'cmb-scripts' ) ); - wp_enqueue_script( 'cmb-datetime', trailingslashit( CMB_URL ) . 'js/field.datetime.js', array( 'jquery', 'jquery-ui-core', 'jquery-ui-datepicker', 'cmb-scripts' ) ); - } - - /** - * Print out field HTML. - */ - public function html() { - ?> - - id_attr( 'date' ); ?> boolean_attr(); ?> class_attr( 'cmb_text_small cmb_datepicker' ); ?> type="text" name_attr( '[date]' ); ?> value="value ? esc_attr( date( 'm\/d\/Y', $this->value ) ) : '' ?>" /> - id_attr( 'time' ); ?> boolean_attr(); ?> class_attr( 'cmb_text_small cmb_timepicker' ); ?> type="text" name_attr( '[time]' ); ?> value="value ? esc_attr( date( 'h:i A', $this->value ) ) : '' ?>" /> - - values as $key => &$value ) { - if ( empty( $value['date'] ) ) { - unset( $this->values[ $key ] ); - } else { - $value = strtotime( $value['date'] . ' ' . $value['time'] ); - } - } - - $this->values = array_filter( $this->values ); - sort( $this->values ); - - parent::parse_save_values(); - - } -} - -/** - * Standard text field. - * - * Args: - * - int "rows" - number of rows in the - - - * - * @since 1.0.0 - * - * @extends CMB_Textarea_Field - */ -class CMB_Textarea_Field_Code extends CMB_Textarea_Field { - - /** - * Print out field HTML. - */ - public function html() { - - $this->args['class'] .= ' code'; - - parent::html(); - - } -} - -/** - * Colour picker - * - * @since 1.0.0 - * - * @extends CMB_Field - */ -class CMB_Color_Picker extends CMB_Field { - - /** - * Enqueue all scripts required by the field. - * - * @uses wp_enqueue_script() - */ - public function enqueue_scripts() { - - parent::enqueue_scripts(); - - wp_enqueue_script( 'cmb-colorpicker', trailingslashit( CMB_URL ) . 'js/field.colorpicker.js', array( 'jquery', 'wp-color-picker', 'cmb-scripts' ) ); - wp_enqueue_style( 'wp-color-picker' ); - } - - /** - * Print out field HTML. - */ - public function html() { - ?> - - id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_colorpicker cmb_text_small' ); ?> type="text" name_attr(); ?> value="get_value() ); ?>" /> - - array(), - ) - ); - } - - /** - * Print out field HTML. - */ - public function html() { - - if ( $this->has_data_delegate() ) { - $this->args['options'] = $this->get_delegate_data(); - } ?> - - args['options'] as $key => $value ) : ?> - - id_attr( 'item-' . $key ); ?> boolean_attr(); ?> class_attr(); ?> type="radio" name_attr(); ?> value="" get_value() ); ?> /> - - - - - - - id_attr(); ?> boolean_attr(); ?> class_attr(); ?> type="checkbox" name_attr(); ?> value="1" get_value() ); ?> /> - - - - -
-

class_attr(); ?>> - title ); ?> -

-
- - array(), - ) - ); - } - - /** - * Enqueue all scripts required by the field. - * - * @uses wp_enqueue_script() - */ - function enqueue_scripts() { - - parent::enqueue_scripts(); - - wp_enqueue_script( 'cmb-wysiwyg', trailingslashit( CMB_URL ) . 'js/field-wysiwyg.js', array( 'jquery', 'cmb-scripts' ) ); - } - - /** - * Print out field HTML. - */ - public function html() { - - $id = $this->get_the_id_attr(); - $name = $this->get_the_name_attr(); - - $field_id = $this->get_js_id(); - - printf( - '
', - esc_attr( $id ), - esc_attr( $name ), - esc_attr( $field_id ) - ); - - if ( $this->is_placeholder() ) { - - // For placeholder, output the markup for the editor in a JS var. - ob_start(); - $this->args['options']['textarea_name'] = 'cmb-placeholder-name-' . $field_id; - wp_editor( '', 'cmb-placeholder-id-' . $field_id, $this->args['options'] ); - $editor = ob_get_clean(); - $editor = str_replace( array( "\n", "\r" ), '', $editor ); - $editor = str_replace( array( "'" ), '"', $editor ); - - ?> - - - - args['options']['textarea_name'] = $name; - echo wp_editor( $this->get_value(), $id, $this->args['options'] ); - - } - - echo '
'; - - } - - /** - * Check if this is a placeholder field. - * Either the field itself, or because it is part of a repeatable group. - * - * @return bool - */ - public function is_placeholder() { - - if ( isset( $this->parent ) && ! is_int( $this->parent->field_index ) ) { - return true; - } else { - return ! is_int( $this->field_index ); - } - - } -} - -/** - * Standard select field. - * - * @supports "data_delegate" - * @args - * 'options' => array Array of options to show in the select, optionally use data_delegate instead - * 'allow_none' => bool|string Allow no option to be selected (will place a "None" at the top of the select) - * 'multiple' => bool whether multiple can be selected - * - * @since 1.0.0 - * - * @extends CMB_Field - */ -class CMB_Select extends CMB_Field { - - /** - * CMB_Select constructor. - */ - public function __construct() { - - $args = func_get_args(); - - call_user_func_array( array( 'parent', '__construct' ), $args ); - - } - - /** - * Get default arguments for field including custom parameters. - * - * @return array Default arguments for field. - */ - public function get_default_args() { - return array_merge( - parent::get_default_args(), - array( - 'options' => array(), - 'multiple' => false, - 'select2_options' => array(), - 'allow_none' => false, - ) - ); - } - - /** - * Ensure values are saved as an array if multiple is set. - */ - public function parse_save_values() { - - if ( isset( $this->parent ) && isset( $this->args['multiple'] ) && $this->args['multiple'] ) { - $this->values = array( $this->values ); - } - - } - - /** - * Get options for field. - * - * @return mixed - */ - public function get_options() { - - if ( $this->has_data_delegate() ) { - $this->args['options'] = $this->get_delegate_data(); - } - - return $this->args['options']; - } - - /** - * Enqueue all scripts required by the field. - * - * @uses wp_enqueue_script() - */ - public function enqueue_scripts() { - - parent::enqueue_scripts(); - - wp_enqueue_script( 'select2', trailingslashit( CMB_URL ) . 'js/vendor/select2/select2.js', array( 'jquery' ) ); - wp_enqueue_script( 'field-select', trailingslashit( CMB_URL ) . 'js/field.select.js', array( 'jquery', 'select2', 'cmb-scripts' ) ); - } - - /** - * Enqueue all styles required by the field. - * - * @uses wp_enqueue_style() - */ - public function enqueue_styles() { - - parent::enqueue_styles(); - - wp_enqueue_style( 'select2', trailingslashit( CMB_URL ) . 'js/vendor/select2/select2.css' ); - } - - /** - * Print out field HTML. - */ - public function html() { - - if ( $this->has_data_delegate() ) { - $this->args['options'] = $this->get_delegate_data(); - } - - $this->output_field(); - - $this->output_script(); - - } - - /** - * Compile field HTML. - */ - public function output_field() { - - $val = (array) $this->get_value(); - - $name = $this->get_the_name_attr(); - $name .= ! empty( $this->args['multiple'] ) ? '[]' : null; - - $none = is_string( $this->args['allow_none'] ) ? $this->args['allow_none'] : __( 'None', 'cmb' ); - - ?> - - - - args['select2_options'], array( - 'placeholder' => __( 'Type to search', 'cmb' ), - 'allowClear' => true, - ) ); - - ?> - - - - '', - 'hide_empty' => false, - ) - ); - } - - /** - * CMB_Taxonomy constructor. - */ - public function __construct() { - - $args = func_get_args(); - - call_user_func_array( array( 'parent', '__construct' ), $args ); - - $this->args['data_delegate'] = array( $this, 'get_delegate_data' ); - - } - - /** - * Retrieve custom field data. - * - * @return array Terms for field data. - */ - public function get_delegate_data() { - - $terms = $this->get_terms(); - - if ( is_wp_error( $terms ) ) { - return array(); - } - - $term_options = array(); - - foreach ( $terms as $term ) { - $term_options[ $term->term_id ] = $term->name; - } - - return $term_options; - - } - - /** - * Get terms for select field. - * - * @todo::cache this or find a cached method - * - * @return array|int|WP_Error - */ - private function get_terms() { - - return get_terms( $this->args['taxonomy'], array( 'hide_empty' => $this->args['hide_empty'] ) ); - - } -} - -/** - * Post Select field. - * - * @supports "data_delegate" - * @args - * 'options' => array Array of options to show in the select, optionally use data_delegate instead - * 'allow_none' => bool Allow no option to be selected (will place a "None" at the top of the select) - * 'multiple' => bool whether multiple can be selected - * - * @since 1.0.0 - * - * @extends CMB_Select - */ -class CMB_Post_Select extends CMB_Select { - - /** - * CMB_Post_Select constructor. - */ - public function __construct() { - - $args = func_get_args(); - - call_user_func_array( array( 'parent', '__construct' ), $args ); - - if ( ! $this->args['use_ajax'] ) { - - $this->args['data_delegate'] = array( $this, 'get_delegate_data' ); - - } - - } - - /** - * Get default arguments for field including custom parameters. - * - * @return array Default arguments for field. - */ - public function get_default_args() { - return array_merge( - parent::get_default_args(), - array( - 'query' => array(), - 'use_ajax' => false, - 'multiple' => false, - ) - ); - } - - /** - * Get posts and verify for use in select field. - * - * @todo:: validate this data before returning. - * - * @return array Array of posts for field. - */ - public function get_delegate_data() { - - $data = array(); - - foreach ( $this->get_posts() as $post_id ) { - $data[ $post_id ] = get_the_title( $post_id ); - } - - return $data; - - } - - /** - * Get posts for use in select field. - * - * @return array - */ - private function get_posts() { - - $this->args['query']['fields'] = 'ids'; - $query = new WP_Query( $this->args['query'] ); - - return isset( $query->posts ) ? $query->posts : array(); - - } - - /** - * Format the field values for Select2 to read. - */ - public function parse_save_value() { - - // AJAX multi select2 data is submitted as a string of comma separated post IDs. - // If empty, set to false instead of empty array to ensure the meta entry is deleted. - if ( $this->args['use_ajax'] && $this->args['multiple'] ) { - $this->value = ( ! empty( $this->value ) ) ? explode( ',', $this->value ) : false; - } - - } - - /** - * Assemble and output of field HTML. - */ - public function output_field() { - - // If AJAX, must use input type not standard select. - if ( $this->args['use_ajax'] ) : - - ?> - - id_attr(); ?> - value ) ) ); ?> - get_the_name_attr() ) ); ?> - get_js_id() ) ); ?> - boolean_attr(); ?> - class="cmb_select" - style="width: 100%" - /> - - - - - - 0, 'posts' => array() ) ); - exit; - } - - $args['fields'] = 'ids'; // Only need to retrieve post IDs. - - $query = new WP_Query( $args ); - - $json = array( 'total' => $query->found_posts, 'posts' => array() ); - - foreach ( $query->posts as $post_id ) { - array_push( $json['posts'], array( 'id' => $post_id, 'text' => html_entity_decode( get_the_title( $post_id ) ) ) ); - } - - echo json_encode( $json ); - - exit; - -} -add_action( 'wp_ajax_cmb_post_select', 'cmb_ajax_post_select' ); - -/** - * Field to group child fields - * pass $args[fields] array for child fields - * pass $args['repeatable'] for cloing all child fields (set) - * - * @todo remove global $post reference, somehow - * - * @since 1.0.0 - * - * @extends CMB_Field - */ -class CMB_Group_Field extends CMB_Field { - - static $added_js; - - /** - * Fields arguments and information. - * - * @var array - */ - private $fields = array(); - - /** - * CMB_Group_Field constructor. - */ - function __construct() { - - // You can't just put func_get_args() into a function as a parameter. - $args = func_get_args(); - call_user_func_array( array( 'parent', '__construct' ), $args ); - - if ( ! empty( $this->args['fields'] ) ) { - foreach ( $this->args['fields'] as $f ) { - - $class = _cmb_field_class_for_type( $f['type'] ); - $this->add_field( new $class( $f['id'], $f['name'], array(), $f ) ); - - } - } - - } - - /** - * Get default arguments for field including custom parameters. - * - * @return array Default arguments for field. - */ - public function get_default_args() { - return array_merge( - parent::get_default_args(), - array( - 'fields' => array(), - 'string-repeat-field' => __( 'Add New Group', 'cmb' ), - 'string-delete-field' => __( 'Remove Group', 'cmb' ), - ) - ); - } - - /** - * Enqueue all scripts required by the field. - * - * @uses wp_enqueue_script() - */ - public function enqueue_scripts() { - - parent::enqueue_scripts(); - - foreach ( $this->args['fields'] as $f ) { - $class = _cmb_field_class_for_type( $f['type'] ); - $field = new $class( '', '', array(), $f ); - $field->enqueue_scripts(); - } - - } - - /** - * Enqueue all styles required by the field. - * - * @uses wp_enqueue_style() - */ - public function enqueue_styles() { - - parent::enqueue_styles(); - - foreach ( $this->args['fields'] as $f ) { - $class = _cmb_field_class_for_type( $f['type'] ); - $field = new $class( '', '', array(), $f ); - $field->enqueue_styles(); - } - - } - - /** - * Display output for group. - */ - public function display() { - - global $post; - - $field = $this->args; - $values = $this->get_values(); - - $this->title(); - $this->description(); - - if ( ! $this->args['repeatable'] && empty( $values ) ) { - $values = array( null ); - } - - if ( $values ) { - - $i = 0; - foreach ( $values as $value ) { - - $this->field_index = $i; - $this->value = $value; - - ?> - -
- html(); ?> -
- - args['repeatable'] ) { - - $this->field_index = 'x'; // X used to distinguish hidden fields. - $this->value = ''; - - ?> - - - - - - get_fields(); - $value = $this->get_value(); - - // Reset all field values. - foreach ( $fields as $field ) { - $field->set_values( array() ); - } - - // Set values for this field. - if ( ! empty( $value ) ) { - foreach ( $value as $field_id => $field_value ) { - $field_value = ( ! empty( $field_value ) ) ? $field_value : array(); - if ( ! empty( $fields[ $field_id ] ) ) { - $fields[ $field_id ]->set_values( (array) $field_value ); - } - } - } - - ?> - - args['repeatable'] ) : ?> - - - - - - get_fields(); - $values = &$this->get_values(); - - foreach ( $values as &$group_value ) { - foreach ( $group_value as $field_id => &$field_value ) { - - if ( ! isset( $fields[ $field_id ] ) ) { - $field_value = array(); - continue; - } - - $field = $fields[ $field_id ]; - $field->set_values( $field_value ); - $field->parse_save_values(); - - $field_value = $field->get_values(); - - // if the field is a repeatable field, store the whole array of them, if it's not repeatble, - // just store the first (and only) one directly. - if ( ! $field->args['repeatable'] ) { - $field_value = reset( $field_value ); - } - } - } - - } - - /** - * Add assigned fields to group data. - * - * @param CMB_Field $field Field object. - */ - public function add_field( CMB_Field $field ) { - $field->parent = $this; - $this->fields[ $field->id ] = $field; - } - - /** - * Assemble all defined fields for group. - * - * @return array - */ - public function &get_fields() { - return $this->fields; - } - - /** - * Set values for each field in the group. - * - * @param array $values Existing or default values for all fields. - */ - public function set_values( array $values ) { - - $fields = &$this->get_fields(); - $this->values = $values; - - // Reset all field values. - foreach ( $fields as $field ) { - $field->set_values( array() ); - } - - foreach ( $values as $value ) { - foreach ( $value as $field_id => $field_value ) { - $fields[ $field_id ]->set_values( (array) $field_value ); - } - } - - } -} - - -/** - * Google map field class for CMB - * - * It enables the google places API and doesn't store the place - * name. It only stores latitude and longitude of the selected area. - * - * Note - you need a Google API key for field to work correctly. - * - * @since 1.0.2 - * - * @extends CMB_Field - */ -class CMB_Gmap_Field extends CMB_Field { - - /** - * Get default arguments for field including custom parameters. - * - * @return array Default arguments for field. - */ - public function get_default_args() { - return array_merge( - parent::get_default_args(), - array( - 'field_width' => '100%', - 'field_height' => '250px', - 'default_lat' => '51.5073509', - 'default_long' => '-0.12775829999998223', - 'default_zoom' => '8', - 'string-marker-title' => esc_html__( 'Drag to set the exact location', 'cmb' ), - 'string-gmaps-api-not-loaded' => esc_html__( 'Google Maps API not loaded.', 'cmb' ), - 'google_api_key' => '', - ) - ); - } - - /** - * Enqueue all scripts required by the field. - * - * @uses wp_enqueue_script() - */ - public function enqueue_scripts() { - - parent::enqueue_scripts(); - - wp_enqueue_script( 'cmb-google-maps-script', trailingslashit( CMB_URL ) . 'js/field-gmap.js', array( 'jquery' ) ); - - // Check for our key with either a field argument or constant. - $key = ''; - if ( ! empty( $this->args['google_api_key'] ) ) { - $key = $this->args['google_api_key']; - } elseif ( defined( 'CMB_GAPI_KEY' ) ) { - $key = CMB_GAPI_KEY; - } - - wp_localize_script( 'cmb-google-maps-script', 'CMBGmaps', array( - 'key' => $key, - 'defaults' => array( - 'latitude' => $this->args['default_lat'], - 'longitude' => $this->args['default_long'], - 'zoom' => $this->args['default_zoom'], - ), - 'strings' => array( - 'markerTitle' => $this->args['string-marker-title'], - 'googleMapsApiNotLoaded' => $this->args['string-gmaps-api-not-loaded'], - ), - ) ); - - } - - /** - * Print out field HTML. - */ - public function html() { - - // Ensure all args used are set. - $value = wp_parse_args( - $this->get_value(), - array( - 'lat' => null, - 'long' => null, - 'elevation' => null, - 'text' => null, - ) - ); - - $style = array( - sprintf( 'width: %s;', $this->args['field_width'] ), - sprintf( 'height: %s;', $this->args['field_height'] ), - 'border: 1px solid #eee;', - 'margin-top: 8px;', - ); - - ?> - - class_attr( 'map-search' ); ?> id_attr(); ?> name_attr( '[text]' ); ?> value="" /> - -
- - name_attr( '[lat]' ); ?> value="" /> - name_attr( '[long]' ); ?> value="" /> - name_attr( '[elevation]' ); ?> value="" /> - - [class*=cmb-cell-]>.field{border-bottom:0}.postbox>.inside>.cmb_metabox{margin:-10px 0}.cmb_metabox .cmb-row{overflow:hidden;margin:0 -5px;zoom:1}.cmb_metabox .cmb-row:after,.cmb_metabox .cmb-row:before{content:"";display:table}.cmb_metabox .cmb-row:after{clear:both}.cmb_metabox [class*=cmb-cell-]{float:left;padding:0 5px;-moz-box-sizing:border-box;box-sizing:border-box}.cmb_metabox .cmb-cell-1{width:8.333333333%}.cmb_metabox .cmb-cell-2{width:16.666666667%}.cmb_metabox .cmb-cell-3{width:25%}.cmb_metabox .cmb-cell-4{width:33.333333333%}.cmb_metabox .cmb-cell-5{width:41.666666667%}.cmb_metabox .cmb-cell-6{width:50%}.cmb_metabox .cmb-cell-7{width:58.333333333%}.cmb_metabox .cmb-cell-8{width:66.666666667%}.cmb_metabox .cmb-cell-9{width:75%}.cmb_metabox .cmb-cell-10{width:83.333333333%}.cmb_metabox .cmb-cell-11{width:91.666666667%}.cmb_metabox .cmb-cell-12{width:100%}@media all and (max-width:850px){.cmb_metabox [class*=cmb-cell-]{width:100%}}@media all and (min-width:850px){.cmb_metabox [class*=cmb-cell-].cmb-has-label+.cmb-no-label{padding-top:32px}}.cmb_metabox .field-item{position:relative}.cmb_metabox_description{color:#AAA;font-style:italic;margin:0 0 16px!important}.cmb_metabox input[type=text],.cmb_metabox select,.cmb_metabox textarea{width:100%;margin:0}.cmb_metabox input.cmb_text_small{width:100px}.cmb_metabox input.cmb_text_medium{width:230px;margin-right:15px}.cmb_metabox input[type=checkbox],.cmb_metabox input[type=radio]{margin:0 5px 0 0;padding:0}.cmb_metabox .field-title{margin-bottom:16px;margin-top:0;font-weight:700}.cmb_metabox .field-title label{vertical-align:baseline}.cmb_metabox .select2-search-choice-close{-webkit-transition:none;transition:none;top:3px}.cmb_metabox [disabled]{background:#F7F7F7}.cmb_metabox .field.repeatable>.field-item{padding-right:30px;margin-bottom:10px}.cmb_metabox .repeat-field{display:block!important;clear:both}.CMB_Date_Field.repeatable .field-item,.CMB_Date_Timestamp_Field.repeatable .field-item,.CMB_Datetime_Timestamp_Field.repeatable .field-item,.CMB_Text_Small_Field.repeatable .field-item,.CMB_Time_Field.repeatable .field-item{float:left;clear:both}.cmb-delete-field{display:inline-block;text-decoration:none;font-size:11px;line-height:20px;height:22px;width:22px;margin:0;padding:0;cursor:pointer;border-width:1px;border-style:solid;-webkit-border-radius:3px;-webkit-appearance:none;border-radius:3px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;color:#555;border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);vertical-align:top;position:absolute;top:3px;right:0;text-indent:100%;overflow:hidden;white-space:nowrap}.cmb-delete-field:active,.cmb-delete-field:focus,.cmb-delete-field:hover{background:#fafafa;border-color:#999;color:#222}.cmb-delete-field .cmb-delete-field-icon{content:' ';display:block;position:absolute;height:8px;width:8px;top:50%;left:50%;margin-top:-4px;margin-left:-4px;background-image:url( '../../images/cmb-icon-remove.png');background-repeat:no-repeat;text-indent:8px;overflow:hidden}@media only screen and (-webkit-min-device-pixel-ratio :1.5),only screen and (min-device-pixel-ratio :1.5){.cmb-delete-field .cmb-delete-field-icon{background-image:url( '../../images/cmb-icon-remove@2x.png');background-size:8px 8px}}.cmb-sortable>.field-item{padding-left:15px}.cmb-sortable .cmb-handle{height:100%;width:3px;position:absolute;top:0;left:0;background:0 0;cursor:move;border-left:3px solid #DFDFDF;border-right:3px solid #DFDFDF}.cmb-sortable .ui-sortable-helper{opacity:.75}.cmb-sortable .ui-sortable-helper:before{content:' ';position:absolute;width:100%;height:100%;top:-6px;left:-6px;background-color:#F8F8F8;border:1px solid #DEDEDE;padding:5px;z-index:-1}.cmb-sortable>.ui-sortable-placeholder{border:1px dashed #DDD!important;background:transparent!important;visibility:visible!important;margin-bottom:8px!important}.cmb_metabox .CMB_Group_Field>.field-title{font-size:20px;clear:left;color:#222;font-family:'Open Sans',sans-serif;font-weight:400;margin:8px 0}.cmb_metabox .CMB_Group_Field.repeatable>.field-title{margin-bottom:16px}.cmb_metabox .CMB_Group_Field.repeatable>.field-item{padding:30px 10px 0;margin-bottom:16px;border:1px solid #DDD;background:#FAFAFA;position:relative}.cmb_metabox .CMB_Group_Field.repeatable>.field-item:before{content:' ';display:block;position:absolute;top:0;left:0;right:0;height:33px;border-bottom:1px solid #DDD}.cmb_metabox .CMB_Group_Field.repeatable>.field-item>.cmb-delete-field{top:5px;right:5px;width:auto;text-indent:0;padding-left:24px;padding-right:8px;height:22px;line-height:20px;font-size:11px;z-index:1}.cmb_metabox .CMB_Group_Field.repeatable>.field-item>.cmb-delete-field .cmb-delete-field-icon{left:12px}.CMB_Group_Field.cmb-sortable>.field-item{position:relative;padding-top:30px}.CMB_Group_Field.cmb-sortable>.field-item>.cmb-handle{top:-1px;left:-1px;right:-1px;height:34px;width:auto;border:0;border-bottom:1px solid #DDD}.CMB_Group_Field.cmb-sortable>.field-item.ui-sortable-helper>.cmb-handle{padding:0 5px}.cmb_metabox .CMB_Group_Field.repeatable.cmb-sortable>.field-item:before{display:none!important}.CMB_Group_Field.cmb-sortable>.ui-sortable-placeholder{margin-bottom:16px!important}.CMB_Group_Field .cmb_metabox>.cmb-row:last-child{margin-bottom:0}.CMB_File_Field .field-item,.CMB_Image_Field .field-item{float:left;margin:0 16px 16px 0!important;text-align:center;padding:0!important;z-index:1}.cmb-file-wrap{position:relative;overflow:hidden;width:150px;height:150px;line-height:150px}.cmb-file-wrap-placeholder{content:' ';position:absolute;top:0;left:0;display:block;height:142px;width:142px;border:4px dashed #DDD;z-index:-1}.cmb-file-wrap-placeholder .dimensions{line-height:normal;position:absolute;bottom:10px;right:10px;font-size:18px;font-weight:700;opacity:.2}.cmb-file-holder{position:relative;overflow:hidden;box-shadow:inset 0 0 15px rgba(0,0,0,.1),inset 0 0 0 1px rgba(0,0,0,.05);background:#eee;width:100%;height:100%}.cmb-file-holder.type-file img{position:absolute;top:43%;margin-top:-30px;left:50%;margin-left:-23px}.cmb-file-holder.type-img img{width:100%;height:auto;margin-top:0;vertical-align:top}.cmb-file-holder.type-img::after{content:'';display:block;position:absolute;top:0;left:0;right:0;bottom:0;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);overflow:hidden;z-index:1}.cmb-file-name{position:absolute;left:0;right:0;bottom:0;line-height:1.4;overflow:hidden;max-height:100%;word-wrap:break-word;text-align:center;font-weight:700;background:rgba(255,255,255,.8);box-shadow:inset 0 0 0 1px rgba(0,0,0,.15)}.cmb-file-name strong{padding:5px 10px;display:block}.CMB_File_Field .cmb-delete-field,.CMB_Image_Field .cmb-delete-field{z-index:10;top:10px;right:10px}.CMB_File_Field .cmb-remove-file,.CMB_Image_Field .cmb-remove-file{position:absolute;z-index:1;top:5px;right:5px}.CMB_File_Field.repeatable .cmb-remove-file,.CMB_Image_Field.repeatable .cmb-remove-file{display:none!important}.cmb-file-wrap .cmb-file-upload{vertical-align:middle}.cmb-loading::before{content:' ';display:block;background:url( '../../images/wpspin.gif' ) no-repeat;width:16px;height:16px;position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}@media only screen and (-webkit-min-device-pixel-ratio :1.5),only screen and (min-device-pixel-ratio :1.5){.cmb-loading::before{background-image:url( '../../images/wpspin-2x.gif');background-size:16px 16px}}.CMB_File_Field.cmb-sortable .cmb-handle,.CMB_Image_Field.cmb-sortable .cmb-handle{border:0;background:0 0;height:100%;width:100%;z-index:1}.CMB_File_Field.cmb-sortable .cmb-file-upload,.CMB_Image_Field.cmb-sortable .cmb-file-upload{position:relative;z-index:5}.CMB_File_Field.cmb-sortable .ui-sortable-helper:before,.CMB_Image_Field.cmb-sortable .ui-sortable-helper:before{top:0;left:0;padding:0;border:0;background:#F9F9F9}.CMB_Title .field-title{margin:8px 0!important}.CMB_Title{border-bottom:0;padding-bottom:0;margin-top:16px}.CMB_Title h2{margin:0!important;padding:0!important}.CMB_Color_Picker .field-item{float:left;clear:both}.CMB_Color_Picker:after,.CMB_Color_Picker:before{content:"";display:table}.CMB_Color_Picker:after{clear:both}.CMB_Color_Picker{zoom:1}div.time-picker{position:absolute;height:191px;width:4em;overflow:auto;background:#fff;border:1px solid #aaa;z-index:99;margin:0}div.time-picker-12hours{width:6em}div.time-picker ul{list-style-type:none;margin:0;padding:0}div.time-picker li{cursor:pointer;height:10px;font:12px/1 Helvetica,Arial,sans-serif;padding:4px 3px}div.time-picker li.selected{background:#0063CE;color:#fff}.cmb_metabox input.cmb_timepicker{width:100px;margin-right:0}.CMB_Datetime_Timestamp_Field input+input{margin-left:4px}.cmb_select{width:100%}.select2-offscreen{display:none} \ No newline at end of file +.cmb_metabox .field{overflow:hidden;padding:16px 0}.postbox>.inside>.cmb_metabox{margin:-10px 0;padding:5px 5px 0}.cmb_metabox .cmb-row{overflow:hidden;position:relative;margin:0 -10px;zoom:1}.cmb_metabox .cmb-row:after,.cmb_metabox .cmb-row:before{content:"";display:table}.cmb_metabox .cmb-row:after{content:" ";position:absolute;clear:both;display:block;border-bottom:1px solid #DFDFDF;left:10px;right:10px;bottom:0}.cmb_metabox>.cmb-row:last-child{margin-bottom:-15px}.cmb_metabox>.cmb-row:last-child:after{display:none}.cmb_metabox [class*=cmb-cell-]{float:left;padding:0 10px;-moz-box-sizing:border-box;box-sizing:border-box}.cmb_metabox .cmb-cell-1{width:8.333333333%}.cmb_metabox .cmb-cell-2{width:16.666666667%}.cmb_metabox .cmb-cell-3{width:25%}.cmb_metabox .cmb-cell-4{width:33.333333333%}.cmb_metabox .cmb-cell-5{width:41.666666667%}.cmb_metabox .cmb-cell-6{width:50%}.cmb_metabox .cmb-cell-7{width:58.333333333%}.cmb_metabox .cmb-cell-8{width:66.666666667%}.cmb_metabox .cmb-cell-9{width:75%}.cmb_metabox .cmb-cell-10{width:83.333333333%}.cmb_metabox .cmb-cell-11{width:91.666666667%}.cmb_metabox .cmb-cell-12{width:100%}@media all and (max-width:850px){.cmb_metabox [class*=cmb-cell-]{width:100%}}@media all and (min-width:850px){.cmb_metabox [class*=cmb-cell-].cmb-has-label+.cmb-no-label{padding-top:32px}}.cmb_metabox .field-item{position:relative}.cmb_metabox_description{color:#AAA;font-style:italic;margin:0 0 16px!important}.cmb_metabox input[type=email],.cmb_metabox input[type=text],.cmb_metabox select,.cmb_metabox textarea{width:100%;margin:0}.cmb_metabox input.cmb_text_small{width:100px}.cmb_metabox input.cmb_text_medium{width:230px;margin-right:15px}.cmb_metabox input[type=checkbox],.cmb_metabox input[type=radio]{margin:0 5px 0 0;padding:0}.cmb_metabox .field-title{margin-bottom:16px;margin-top:0;font-weight:700}.cmb_metabox .field-title label{vertical-align:baseline}.cmb_metabox .select2-search-choice-close{-webkit-transition:none;transition:none;top:3px}.cmb_metabox [disabled]{background:#F7F7F7}.cmb_metabox .field.repeatable>.field-item{padding-right:30px;margin-bottom:10px}.cmb_metabox .repeat-field{display:block!important;clear:both}.CMB_Date_Field.repeatable .field-item,.CMB_Date_Timestamp_Field.repeatable .field-item,.CMB_Datetime_Timestamp_Field.repeatable .field-item,.CMB_Text_Small_Field.repeatable .field-item,.CMB_Time_Field.repeatable .field-item{float:left;clear:both}.cmb-delete-field{display:inline-block;text-decoration:none;font-size:11px;line-height:20px;height:22px;width:22px;margin:0;padding:0;cursor:pointer;border-width:1px;border-style:solid;-webkit-border-radius:3px;-webkit-appearance:none;border-radius:3px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;color:#555;border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);vertical-align:top;position:absolute;top:3px;right:0;text-indent:100%;overflow:hidden;white-space:nowrap}.cmb-delete-field:active,.cmb-delete-field:focus,.cmb-delete-field:hover{background:#fafafa;border-color:#999;color:#222}.cmb-delete-field .cmb-delete-field-icon{content:' ';display:block;position:absolute;height:8px;width:8px;top:50%;left:50%;margin-top:-4px;margin-left:-4px;background-image:url( '../../images/cmb-icon-remove.png');background-repeat:no-repeat;text-indent:8px;overflow:hidden}@media only screen and (-webkit-min-device-pixel-ratio :1.5),only screen and (min-device-pixel-ratio :1.5){.cmb-delete-field .cmb-delete-field-icon{background-image:url( '../../images/cmb-icon-remove@2x.png');background-size:8px 8px}}.cmb-sortable>.field-item{padding-left:15px}.cmb-sortable .cmb-handle{height:100%;width:3px;position:absolute;top:0;left:0;background:0 0;cursor:move;border-left:3px solid #DFDFDF;border-right:3px solid #DFDFDF}.cmb-sortable .ui-sortable-helper{opacity:.75}.cmb-sortable .ui-sortable-helper:before{content:' ';position:absolute;width:100%;height:100%;top:-6px;left:-6px;background-color:#F8F8F8;border:1px solid #DEDEDE;padding:5px;z-index:-1}.cmb-sortable>.ui-sortable-placeholder{border:1px dashed #DDD!important;background:transparent!important;visibility:visible!important;margin-bottom:8px!important}.cmb_metabox .CMB_Group_Field>.field-title{font-size:20px;clear:left;color:#222;font-family:'Open Sans',sans-serif;font-weight:400;margin:8px 0}.cmb_metabox .CMB_Group_Field.repeatable>.field-title{margin-bottom:16px}.cmb_metabox .CMB_Group_Field.repeatable>.field-item{padding:30px 15px 0;margin-bottom:16px;border:1px solid #DDD;background:#FAFAFA;position:relative}.cmb_metabox .CMB_Group_Field.repeatable>.field-item:before{content:' ';display:block;position:absolute;top:0;left:0;right:0;height:33px;border-bottom:1px solid #DDD}.cmb_metabox .CMB_Group_Field.repeatable>.field-item>.cmb-delete-field{top:5px;right:5px;width:auto;text-indent:0;padding-left:24px;padding-right:8px;height:22px;line-height:20px;font-size:11px;z-index:1}.cmb_metabox .CMB_Group_Field.repeatable>.field-item>.cmb-delete-field .cmb-delete-field-icon{left:12px}.CMB_Group_Field.cmb-sortable>.field-item{position:relative;padding-top:30px}.CMB_Group_Field.cmb-sortable>.field-item>.cmb-handle{top:-1px;left:-1px;right:-1px;height:34px;width:auto;border:0;border-bottom:1px solid #DDD}.CMB_Group_Field.cmb-sortable>.field-item.ui-sortable-helper>.cmb-handle{padding:0 5px}.cmb_metabox .CMB_Group_Field.repeatable.cmb-sortable>.field-item:before{display:none!important}.CMB_Group_Field.cmb-sortable>.ui-sortable-placeholder{margin-bottom:16px!important}.CMB_Group_Field .cmb_metabox>.cmb-row:last-child{margin-bottom:0}.CMB_File_Field .field-item,.CMB_Image_Field .field-item{float:left;margin:0 16px 16px 0!important;text-align:center;padding:0!important;z-index:1;max-width:100%}.cmb-file-wrap{position:relative;overflow:hidden;width:150px;height:150px;line-height:150px}.cmb-file-wrap-placeholder{content:' ';position:absolute;top:0;left:0;display:block;height:142px;width:142px;border:4px dashed #DDD;z-index:-1;box-sizing:border-box}.cmb-file-wrap-placeholder .dimensions{line-height:normal;position:absolute;bottom:10px;right:10px;font-size:18px;font-weight:700;opacity:.2}.cmb-file-holder{position:relative;overflow:hidden;box-shadow:inset 0 0 15px rgba(0,0,0,.1),inset 0 0 0 1px rgba(0,0,0,.05);background:#eee;width:100%;height:100%}.cmb-file-holder.type-file img{position:absolute;top:43%;margin-top:-30px;left:50%;margin-left:-23px}.cmb-file-holder.type-img img{width:100%;height:auto;margin-top:0;vertical-align:top}.cmb-file-holder.type-img::after{content:'';display:block;position:absolute;top:0;left:0;right:0;bottom:0;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);overflow:hidden;z-index:1}.cmb-file-name{position:absolute;left:0;right:0;bottom:0;line-height:1.4;overflow:hidden;max-height:100%;word-wrap:break-word;text-align:center;font-weight:700;background:rgba(255,255,255,.8);box-shadow:inset 0 0 0 1px rgba(0,0,0,.15)}.cmb-file-name strong{padding:5px 10px;display:block}.CMB_File_Field .cmb-delete-field,.CMB_Image_Field .cmb-delete-field{z-index:10;top:10px;right:10px}.CMB_File_Field .cmb-remove-file,.CMB_Image_Field .cmb-remove-file{position:absolute;z-index:1;top:5px;right:5px}.CMB_File_Field.repeatable .cmb-remove-file,.CMB_Image_Field.repeatable .cmb-remove-file{display:none!important}.cmb-file-wrap .cmb-file-upload{vertical-align:middle}.cmb-loading::before{content:' ';display:block;background:url( '../../images/wpspin.gif' ) no-repeat;width:16px;height:16px;position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}@media only screen and (-webkit-min-device-pixel-ratio :1.5),only screen and (min-device-pixel-ratio :1.5){.cmb-loading::before{background-image:url( '../../images/wpspin-2x.gif');background-size:16px 16px}}.CMB_File_Field.cmb-sortable .cmb-handle,.CMB_Image_Field.cmb-sortable .cmb-handle{border:0;background:0 0;height:100%;width:100%;z-index:1}.CMB_File_Field.cmb-sortable .cmb-file-upload,.CMB_Image_Field.cmb-sortable .cmb-file-upload{position:relative;z-index:5}.CMB_File_Field.cmb-sortable .ui-sortable-helper:before,.CMB_Image_Field.cmb-sortable .ui-sortable-helper:before{top:0;left:0;padding:0;border:0;background:#F9F9F9}.CMB_Title .field-title{margin:8px 0!important}.CMB_Title{border-bottom:0;padding-bottom:0;margin-top:16px}.CMB_Title h2{margin:0!important;padding:0!important}.CMB_Color_Picker .field-item{float:left;clear:both}.CMB_Color_Picker:after,.CMB_Color_Picker:before{content:"";display:table}.CMB_Color_Picker:after{clear:both}.CMB_Color_Picker{zoom:1}div.time-picker{position:absolute;height:191px;width:4em;overflow:auto;background:#fff;border:1px solid #aaa;z-index:99;margin:0}div.time-picker-12hours{width:6em}div.time-picker ul{list-style-type:none;margin:0;padding:0}div.time-picker li{cursor:pointer;height:10px;font:12px/1 Helvetica,Arial,sans-serif;padding:4px 3px}div.time-picker li.selected{background:#0063CE;color:#fff}.cmb_metabox input.cmb_timepicker{width:100px;margin-right:0}.CMB_Datetime_Timestamp_Field input+input{margin-left:4px}.cmb_select{width:100%}.select2-offscreen{display:none} \ No newline at end of file diff --git a/css/src/file.css b/css/src/file.css index ce5f8d4d..3566e669 100644 --- a/css/src/file.css +++ b/css/src/file.css @@ -5,7 +5,8 @@ margin: 0 16px 16px 0 !important; text-align: center; padding: 0 !important; - z-index: 1; + z-index: 1; + max-width: 100%; } .cmb-file-wrap { @@ -13,7 +14,7 @@ overflow: hidden; width: 150px; height: 150px; - line-height: 150px; + line-height: 150px; } .cmb-file-wrap-placeholder { @@ -25,7 +26,8 @@ height: 142px; width: 142px; border: 4px dashed #DDD; - z-index: -1; + z-index: -1; + box-sizing: border-box; } .cmb-file-wrap-placeholder .dimensions { diff --git a/css/src/generic.css b/css/src/generic.css index 4b26337c..63d86f7c 100644 --- a/css/src/generic.css +++ b/css/src/generic.css @@ -8,7 +8,8 @@ margin: 0 0 16px !important; } -.cmb_metabox input[type=text], +.cmb_metabox input[type=text], +.cmb_metabox input[type=email], .cmb_metabox textarea, .cmb_metabox select { width: 100%; diff --git a/css/src/group.css b/css/src/group.css index 06d6ce17..82c03b69 100644 --- a/css/src/group.css +++ b/css/src/group.css @@ -14,7 +14,7 @@ } .cmb_metabox .CMB_Group_Field.repeatable > .field-item { - padding: 30px 10px 0; + padding: 30px 15px 0; margin-bottom: 16px; border: 1px solid #DDD; background: #FAFAFA; diff --git a/css/src/layout.css b/css/src/layout.css index 04d01502..fe742ce9 100644 --- a/css/src/layout.css +++ b/css/src/layout.css @@ -3,21 +3,18 @@ .cmb_metabox .field { overflow: hidden; padding: 16px 0; - border-bottom: 1px solid #DFDFDF; -} - -.cmb-row:last-child > [class*="cmb-cell-"] > .field { - border-bottom: 0; } /* Neaten up the margins when metaboxes are within the standard postbox. */ .postbox > .inside > .cmb_metabox { margin: -10px 0; + padding: 5px 5px 0; } .cmb_metabox .cmb-row { overflow: hidden; - margin: 0 -5px; + position: relative; + margin: 0 -10px; zoom: 1; } @@ -26,13 +23,29 @@ content: ""; display: table; } + .cmb_metabox .cmb-row:after { + content: " "; + position: absolute; clear: both; + display: block; + border-bottom: 1px solid #DFDFDF; + left: 10px; + right: 10px; + bottom: 0; +} + +.cmb_metabox > .cmb-row:last-child { + margin-bottom: -15px; +} + +.cmb_metabox > .cmb-row:last-child:after { + display: none; } .cmb_metabox [class*="cmb-cell-"] { float: left; - padding: 0 5px; + padding: 0 10px; -moz-box-sizing: border-box; box-sizing: border-box; } diff --git a/custom-meta-boxes.php b/custom-meta-boxes.php index 959cfbc9..4c729694 100755 --- a/custom-meta-boxes.php +++ b/custom-meta-boxes.php @@ -22,6 +22,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +if ( ! defined( 'CMB_VERSION' ) ) { + define( 'CMB_VERSION', '1.0.3' ); +} + if ( ! defined( 'CMB_DEV' ) ) { define( 'CMB_DEV', false ); } @@ -37,8 +41,41 @@ /** * Include base, required files. */ -include_once( CMB_PATH . '/classes.fields.php' ); -include_once( CMB_PATH . '/class.cmb-meta-box.php' ); +include_once( CMB_PATH . 'class-cmb-meta-box.php' ); + +/** + * Field types that are extended. + */ +include_once( CMB_PATH . 'fields/class-cmb-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-file-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-select.php' ); +include_once( CMB_PATH . 'fields/class-cmb-text-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-textarea-field.php' ); + +/** + * Final field types. + */ +include_once( CMB_PATH . 'fields/class-cmb-checkbox.php' ); +include_once( CMB_PATH . 'fields/class-cmb-checkbox-multi.php' ); +include_once( CMB_PATH . 'fields/class-cmb-color-picker.php' ); +include_once( CMB_PATH . 'fields/class-cmb-date-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-date-timestamp-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-datetime-timestamp-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-email-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-gmap-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-group-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-hidden-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-image-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-number-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-post-select.php' ); +include_once( CMB_PATH . 'fields/class-cmb-radio-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-taxonomy.php' ); +include_once( CMB_PATH . 'fields/class-cmb-text-small-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-textarea-field-code.php' ); +include_once( CMB_PATH . 'fields/class-cmb-time-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-title.php' ); +include_once( CMB_PATH . 'fields/class-cmb-url-field.php' ); +include_once( CMB_PATH . 'fields/class-cmb-wysiwyg.php' ); /** * Make it possible to add fields in locations other than post edit screen. Optional. @@ -91,29 +128,32 @@ function _cmb_available_fields() { * @param $types All available field types. */ return apply_filters( 'cmb_field_types', array( - 'text' => 'CMB_Text_Field', - 'text_small' => 'CMB_Text_Small_Field', - 'text_url' => 'CMB_URL_Field', - 'url' => 'CMB_URL_Field', - 'radio' => 'CMB_Radio_Field', - 'checkbox' => 'CMB_Checkbox', - 'file' => 'CMB_File_Field', - 'image' => 'CMB_Image_Field', - 'wysiwyg' => 'CMB_wysiwyg', - 'textarea' => 'CMB_Textarea_Field', - 'textarea_code' => 'CMB_Textarea_Field_Code', - 'select' => 'CMB_Select', - 'taxonomy_select' => 'CMB_Taxonomy', - 'post_select' => 'CMB_Post_Select', - 'date' => 'CMB_Date_Field', - 'date_unix' => 'CMB_Date_Timestamp_Field', - 'datetime_unix' => 'CMB_Datetime_Timestamp_Field', - 'time' => 'CMB_Time_Field', - 'colorpicker' => 'CMB_Color_Picker', - 'title' => 'CMB_Title', - 'group' => 'CMB_Group_Field', - 'gmap' => 'CMB_Gmap_Field', - 'number' => 'CMB_Number_Field', + 'text' => 'CMB_Text_Field', + 'text_small' => 'CMB_Text_Small_Field', + 'text_url' => 'CMB_URL_Field', + 'email' => 'CMB_Email_Field', + 'url' => 'CMB_URL_Field', + 'radio' => 'CMB_Radio_Field', + 'checkbox' => 'CMB_Checkbox', + 'checkbox_multi' => 'CMB_Checkbox_Multi', + 'file' => 'CMB_File_Field', + 'hidden' => 'CMB_Hidden_Field', + 'image' => 'CMB_Image_Field', + 'wysiwyg' => 'CMB_wysiwyg', + 'textarea' => 'CMB_Textarea_Field', + 'textarea_code' => 'CMB_Textarea_Field_Code', + 'select' => 'CMB_Select', + 'taxonomy_select' => 'CMB_Taxonomy', + 'post_select' => 'CMB_Post_Select', + 'date' => 'CMB_Date_Field', + 'date_unix' => 'CMB_Date_Timestamp_Field', + 'datetime_unix' => 'CMB_Datetime_Timestamp_Field', + 'time' => 'CMB_Time_Field', + 'colorpicker' => 'CMB_Color_Picker', + 'title' => 'CMB_Title', + 'group' => 'CMB_Group_Field', + 'gmap' => 'CMB_Gmap_Field', + 'number' => 'CMB_Number_Field', ) ); } diff --git a/example-functions.php b/example-functions.php index 3a19f88d..7a57b9ed 100644 --- a/example-functions.php +++ b/example-functions.php @@ -17,48 +17,336 @@ function cmb_sample_metaboxes( array $meta_boxes ) { /** * Example of all available fields. */ - $fields = array( - array( 'id' => 'field-1', 'name' => 'Text input field', 'type' => 'text' ), - array( 'id' => 'field-2', 'name' => 'Read-only text input field', 'type' => 'text', 'readonly' => true, 'default' => 'READ ONLY' ), - array( 'id' => 'field-3', 'name' => 'Repeatable text input field', 'type' => 'text', 'desc' => 'Add up to 5 fields.', 'repeatable' => true, 'repeatable_max' => 5, 'sortable' => true ), + /** + * Basic Text Field. + */ + array( + 'id' => 'field-text', + 'name' => 'Text input field', + 'type' => 'text', + ), + + /** + * Text Field with all options. + */ + array( + // Required. ID of CMB value to be inserted into database. + 'id' => 'field-2', + + // Required.Type of field to call. + 'type' => 'text', + + // Optional. Title that will show above field. + 'name' => __( 'Name', 'cmb' ), + + // Optional. Contextual information. Will display underneath field. + 'desc' => __( 'Repeatable', 'cmb' ), + + // Optional. Whether field should be repeatable. + 'repeatable' => true, + + // Optional. Maximum number of repetitions allowed. + 'repeatable_max' => 5, + + // Optional. Whether repeated instances should be sortable. + 'sortable' => true, + + // Optional. Don't display field label if set to false. + 'show_label' => false, + + // Optional. Make a filed uneditable by user but value will still be saved. + 'readonly' => false, + + // Optional. Make field uneditable by user and value will NOT be saved. + 'disabled' => false, + + // Optional. Default value for new posts. + 'default' => '', + + // Optional. Number of columns field should take up out of 12. + 'cols' => '12', + + // Optional. Add custom styles to field. + 'style' => '', + + // Optional. Add custom CSS classes to field. + 'class' => '', + + // Optional. Custom data callback for field. + 'data_delegate' => null, + + // Optional. Custom save method for field. + 'save_callback' => null, + ), + + /** + * Small Text Field. + */ + array( + 'id' => 'field-small-text', + 'name' => 'Small text input field', + 'type' => 'text_small', + ), + + /** + * Single Checkbox Field. + */ + array( + 'id' => 'field-checkbox', + 'name' => 'Checkbox field', + 'type' => 'checkbox', + ), + + /** + * Checkbox Multi Field. + */ + array( + 'id' => 'field-7b', + 'name' => 'Checkbox-Multi field', + 'type' => 'checkbox_multi', + 'default' => array( 'option1' ), + 'options' => array( + 'option0' => 'Option with a really long label', + 'option1' => 'Option 1', + 'option2' => 'Option 2', + 'option3' => 'Option 3', + 'option4' => 'Option 4', + ) + ), + + /** + * Colorpicker Field. + */ + array( + 'id' => 'field-colorpicker', + 'name' => 'Color', + 'type' => 'colorpicker', + ), + + /** + * Basic Date Field. + */ + array( + 'id' => 'field-date', + 'name' => 'Date input field', + 'type' => 'date', + ), + + /** + * Basic Time Field. + */ + array( + 'id' => 'field-time', + 'name' => 'Time input field', + 'type' => 'time', + ), + + /** + * Date UNIX Field. + */ + array( + 'id' => 'field-date-unix', + 'name' => 'Date (unix) input field', + 'type' => 'date_unix', + ), + + /** + * Date & Time UNIX Field. + */ + array( + 'id' => 'field-datetime-unix', + 'name' => 'Date & Time (unix) input field', + 'type' => 'datetime_unix', + ), + + /** + * Email Field. + */ + array( + 'id' => 'field-email', + 'name' => 'Email field', + 'type' => 'email', + ), + + /* + * Google Map Field. + */ + array( + 'id' => 'field-gmap', + 'name' => 'Location', + 'type' => 'gmap', + + // Required. Pass a Google Maps API key. Alternatively, set with CMB_GAPI_KEY constant. + 'google_api_key' => '{CUSTOM_KEY}', + + // Optional. Width of address input + map display. + 'field_width' => '100%', + + // Optional. Static height of address input + map display. + 'field_height' => '250px', - array( 'id' => 'field-4', 'name' => 'Small text input field', 'type' => 'text_small' ), - array( 'id' => 'field-5', 'name' => 'URL field', 'type' => 'url' ), + // Optional Default latitude for map to display on page load. + 'default_lat' => '51.5073509', - array( 'id' => 'field-6', 'name' => 'Radio input field', 'type' => 'radio', 'options' => array( 'Option 1', 'Option 2' ) ), - array( 'id' => 'field-7', 'name' => 'Checkbox field', 'type' => 'checkbox' ), + // Optional. Default longitude for map to display on page load. + 'default_long' => '-0.12775829999998223', - array( 'id' => 'field-8', 'name' => 'WYSIWYG field', 'type' => 'wysiwyg', 'options' => array( 'editor_height' => '100' ), 'repeatable' => true, 'sortable' => true ), + // Optional. Default zoom for map to display on page load. 1 = Whole world, 20 = Individual buildings + 'default_zoom' => '8', - array( 'id' => 'field-9', 'name' => 'Textarea field', 'type' => 'textarea' ), - array( 'id' => 'field-10', 'name' => 'Code textarea field', 'type' => 'textarea_code' ), + // Optional. Override Marker title text. + 'string-marker-title' => esc_html__( 'Drag to set the exact location', 'cmb' ), - array( 'id' => 'field-11', 'name' => 'File field', 'type' => 'file', 'file_type' => 'image', 'repeatable' => 1, 'sortable' => 1 ), - array( 'id' => 'field-12', 'name' => 'Image upload field', 'type' => 'image', 'repeatable' => true, 'show_size' => true ), + // Optional. Override maps error message. + 'string-gmaps-api-not-loaded' => esc_html__( 'Google Maps API not loaded.', 'cmb' ), + ), - array( 'id' => 'field-13', 'name' => 'Select field', 'type' => 'select', 'options' => array( 'option-1' => 'Option 1', 'option-2' => 'Option 2', 'option-3' => 'Option 3' ), 'allow_none' => true, 'sortable' => true, 'repeatable' => true ), - array( 'id' => 'field-14', 'name' => 'Select field', 'type' => 'select', 'options' => array( 'option-1' => 'Option 1', 'option-2' => 'Option 2', 'option-3' => 'Option 3' ), 'multiple' => true ), - array( 'id' => 'field-15', 'name' => 'Select taxonomy field', 'type' => 'taxonomy_select', 'taxonomy' => 'category' ), - array( 'id' => 'field-15b', 'name' => 'Select taxonomy field', 'type' => 'taxonomy_select', 'taxonomy' => 'category', 'multiple' => true ), - array( 'id' => 'field-16', 'name' => 'Post select field', 'type' => 'post_select', 'use_ajax' => false, 'query' => array( 'cat' => 1 ) ), - array( 'id' => 'field-17', 'name' => 'Post select field (AJAX)', 'type' => 'post_select', 'use_ajax' => true ), - array( 'id' => 'field-17b', 'name' => 'Post select field (AJAX)', 'type' => 'post_select', 'use_ajax' => true, 'query' => array( 'posts_per_page' => 8 ), 'multiple' => true ), + /** + * Radio Input Field. + */ + array( + 'id' => 'field-radio', + 'name' => 'Radio input field', + 'type' => 'radio', + 'options' => array( // Options for individual radio inputs. + 'Option 1', + 'Option 2', + ), + ), - array( 'id' => 'field-18', 'name' => 'Date input field', 'type' => 'date' ), - array( 'id' => 'field-19', 'name' => 'Time input field', 'type' => 'time' ), - array( 'id' => 'field-20', 'name' => 'Date (unix) input field', 'type' => 'date_unix' ), - array( 'id' => 'field-21', 'name' => 'Date & Time (unix) input field', 'type' => 'datetime_unix' ), + /** + * File Upload Field. + */ + array( + 'id' => 'field-file', + 'name' => 'File field', + 'type' => 'file', + 'library-type' => array( // Optional. Type of media allowed [ 'audio', 'video', 'text', 'application' ]. + 'video', + ), + ), - array( 'id' => 'field-22', 'name' => 'Color', 'type' => 'colorpicker' ), + /** + * File Image Upload Field. + */ + array( + 'id' => 'field-image', + 'name' => 'Image upload field', + 'type' => 'image', + 'repeatable' => true, + 'size' => 'thumbnail', // Optional. Registered media size to display. + 'show_size' => true, // Optional. Whether to show the image dimensions underneath image itself. + ), - array( 'id' => 'field-23', 'name' => 'Location', 'type' => 'gmap', 'google_api_key' => '{CUSTOM_KEY}' ), + /** + * Hidden Field. + */ + array( + 'id' => 'field-hidden', + 'name' => 'Hidden field', + 'type' => 'hidden', + 'default' => 'hidden default value', + ), - array( 'id' => 'field-24', 'name' => 'Title Field', 'type' => 'title' ), + /* + * Select Field. + */ + array( + 'id' => 'field-select', + 'name' => 'Select field', + 'type' => 'select', + 'options' => array( + 'option-1' => 'Option 1', + 'option-2' => 'Option 2', + 'option-3' => 'Option 3', + ), + 'allow_none' => true, // Optional. Allow a user to not select any options. + 'multiple' => true, // Optional. Allow multi-select. + 'select2_options' => array(), // Optional. Array of options to pass to Select2. + ), + + /** + * Select for Taxonomies Field. + */ + array( + 'id' => 'field-select-for-taxonomies', + 'name' => 'Select taxonomy field', + 'type' => 'taxonomy_select', + 'taxonomy' => 'category', // Required. Name of taxonomy to pull options from. + 'hide_empty' => false, // Optional. Whether to hide empty terms or not. + 'multiple' => true, // Optional. Allow multi-select. + + ), + + /** + * Select for Posts Field. + */ + array( + 'id' => 'field-select-for-posts', + 'name' => 'Post select field', + 'type' => 'post_select', + 'use_ajax' => false, // Optional. Use AJAX for instant results or load on pageload. + 'multiple' => true, // Optional. Allow multi-select. + 'query' => array( // Optional. WP_Query options to pass through. + 'cat' => 1, + ), + ), + + /** + * Plain Title Field. + */ + array( + 'id' => 'field-title', + 'name' => 'Title Field', + 'type' => 'title', + ), + + /** + * Textarea Field. + */ + array( + 'id' => 'field-textarea', + 'name' => 'Textarea field', + 'type' => 'textarea', + ), + + /** + * Textarea Code Field. + */ + array( + 'id' => 'field-textarea-code', + 'name' => 'Code textarea field', + 'type' => 'textarea_code', + ), + + /** + * WYSIWYG (What You See is What You Get) TinyMCE Field. + */ + array( + 'id' => 'field-wysiwyg', + 'name' => 'WYSIWYG field', + 'type' => 'wysiwyg', + 'options' => array( // Options to pass into TinyMCE instance. + 'editor_height' => '100', + ), + ), + + /** + * URL Field. + */ + array( + 'id' => 'field-url', + 'name' => 'URL field', + 'type' => 'url', + ), ); + /** + * Metabox instantiation. + */ $meta_boxes[] = array( 'title' => 'CMB Test - all fields', 'pages' => 'post', @@ -68,35 +356,69 @@ function cmb_sample_metaboxes( array $meta_boxes ) { /** * Examples of Groups and Columns. */ - $groups_and_cols = array( - array( 'id' => 'gac-1', 'name' => 'Text input field', 'type' => 'text', 'cols' => 4 ), - array( 'id' => 'gac-2', 'name' => 'Text input field', 'type' => 'text', 'cols' => 4 ), - array( 'id' => 'gac-3', 'name' => 'Text input field', 'type' => 'text', 'cols' => 4 ), + + array( + 'id' => 'gac-1', + 'name' => 'Text input field', + 'type' => 'text', + 'cols' => 4, + ), + + array( + 'id' => 'gac-2', + 'name' => 'Text input field', + 'type' => 'text', + 'cols' => 4, + ), + + array( + 'id' => 'gac-3', + 'name' => 'Text input field', + 'type' => 'text', + 'cols' => 4, + ), + + /** + * Group within a Group. + */ array( 'id' => 'gac-4', 'name' => 'Group (4 columns)', 'type' => 'group', 'cols' => 4, 'fields' => array( - array( 'id' => 'gac-4-f-1', 'name' => 'Textarea field', 'type' => 'textarea' ), + array( + 'id' => 'gac-4-f-1', + 'name' => 'Textarea field', + 'type' => 'textarea', + ), ), ), + array( 'id' => 'gac-5', 'name' => 'Group (8 columns)', 'type' => 'group', 'cols' => 8, 'fields' => array( - array( 'id' => 'gac-4-f-1', 'name' => 'Text input field', 'type' => 'text' ), - array( 'id' => 'gac-4-f-2', 'name' => 'Text input field', 'type' => 'text' ), + array( + 'id' => 'gac-4-f-1', + 'name' => 'Text input field', + 'type' => 'text', + ), + array( + 'id' => 'gac-4-f-2', + 'name' => 'Text input field', + 'type' => 'text', + ), ), ), ); $meta_boxes[] = array( - 'title' => 'Groups and Columns', - 'pages' => 'post', + 'title' => 'Groups and Columns', + 'pages' => 'post', 'fields' => $groups_and_cols, ); @@ -115,13 +437,13 @@ function cmb_sample_metaboxes( array $meta_boxes ) { 'pages' => 'post', 'fields' => array( array( - 'id' => 'gp', - 'name' => 'My Repeatable Group', - 'type' => 'group', + 'id' => 'gp', + 'name' => 'My Repeatable Group', + 'type' => 'group', 'repeatable' => true, - 'sortable' => true, - 'fields' => $group_fields, - 'desc' => 'This is the group description.', + 'sortable' => true, + 'fields' => $group_fields, + 'desc' => 'This is the group description.', ), ), ); @@ -129,4 +451,4 @@ function cmb_sample_metaboxes( array $meta_boxes ) { return $meta_boxes; } -add_filter( 'cmb_meta_boxes', 'cmb_sample_metaboxes' ); \ No newline at end of file +add_filter( 'cmb_meta_boxes', 'cmb_sample_metaboxes' ); diff --git a/fields/class-cmb-checkbox-multi.php b/fields/class-cmb-checkbox-multi.php new file mode 100644 index 00000000..45bb4349 --- /dev/null +++ b/fields/class-cmb-checkbox-multi.php @@ -0,0 +1,122 @@ +title(); + + // Print description if necessary. + $this->description(); + + if ( $this->args['repeatable'] ) { + + $values = ( $this->get_values() ) ? $this->get_values() : array(); + + $i = 0; + + foreach ( $values as $key => $value ) { + + $this->field_index = $i; + $this->value = $value; + ?> + +
+ + args['repeatable'] ) { + $this->delete_button_markup(); + } ?> + html(); ?> + +
+ + repeatable_button_markup(); + } else { + + $this->value = $this->get_values(); + ?> + +
+ + html(); ?> + +
+ + has_data_delegate() ) { + $this->args['options'] = $this->get_delegate_data(); + } + + // Whoops, someone forgot to add some options. We can't do anything without options. + if ( empty( $this->args['options'] ) ) { + return; + } + + foreach ( $this->args['options'] as $i => $label ) : + + $value = $this->get_values(); + ?> + +
+ + id_attr( 'item-' . $i ); ?> + boolean_attr(); ?> + class_attr(); ?> + name_attr( '[' . $i . ']' ); ?> + + /> + + + +
+ + is_new_object() ) { + return $this->values; + } + + return array_flip( (array) $this->args['default'] ); + } +} diff --git a/fields/class-cmb-checkbox.php b/fields/class-cmb-checkbox.php new file mode 100644 index 00000000..bbdacdbe --- /dev/null +++ b/fields/class-cmb-checkbox.php @@ -0,0 +1,31 @@ + + + id_attr(); ?> boolean_attr(); ?> class_attr(); ?> type="checkbox" name_attr(); ?> value="1" get_value() ); ?> /> + + + + + id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_colorpicker cmb_text_small' ); ?> type="text" name_attr(); ?> value="get_value() ); ?>" /> + + args['cols'] ) && $this->args['cols'] <= 6 ) ? 'cmb_datepicker' : 'cmb_text_small cmb_datepicker' ; + ?> + + id_attr(); ?> boolean_attr(); ?> class_attr( $classes ); ?> type="text" name_attr(); ?> value="value ); ?>" /> + + + + id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_text_small cmb_datepicker' ); ?> type="text" name_attr(); ?> value="value ? esc_attr( date( 'm\/d\/Y', $this->value ) ) : '' ?>" /> + + values as &$value ) { + $value = strtotime( $value ); + } + + sort( $this->values ); + + } +} diff --git a/fields/class-cmb-datetime-timestamp-field.php b/fields/class-cmb-datetime-timestamp-field.php new file mode 100644 index 00000000..c50165a9 --- /dev/null +++ b/fields/class-cmb-datetime-timestamp-field.php @@ -0,0 +1,63 @@ + + + id_attr( 'date' ); ?> boolean_attr(); ?> class_attr( 'cmb_text_small cmb_datepicker' ); ?> type="text" name_attr( '[date]' ); ?> value="value ? esc_attr( date( 'm\/d\/Y', $this->value ) ) : '' ?>" /> + id_attr( 'time' ); ?> boolean_attr(); ?> class_attr( 'cmb_text_small cmb_timepicker' ); ?> type="text" name_attr( '[time]' ); ?> value="value ? esc_attr( date( 'h:i A', $this->value ) ) : '' ?>" /> + + values as $key => &$value ) { + if ( empty( $value['date'] ) ) { + unset( $this->values[ $key ] ); + } else { + $value = strtotime( $value['date'] . ' ' . $value['time'] ); + } + } + + $this->values = array_filter( $this->values ); + sort( $this->values ); + + parent::parse_save_values(); + + } +} diff --git a/fields/class-cmb-email-field.php b/fields/class-cmb-email-field.php new file mode 100644 index 00000000..89288642 --- /dev/null +++ b/fields/class-cmb-email-field.php @@ -0,0 +1,25 @@ + + + id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_email code' ); ?> name_attr(); ?> value="get_value() ); ?>" /> + + id = $name; + $this->name = $name . '[]'; + $this->title = $title; + $this->set_arguments( $args ); + + // If the field has a custom value populator callback. + if ( ! empty( $args['values_callback'] ) ) { + $this->values = call_user_func( $args['values_callback'], get_the_id() ); + } else { + $this->values = $values; + } + + $this->value = reset( $this->values ); + } + + /** + * Establish baseline default arguments for a field. + * + * @return array Default arguments. + */ + public function default_args() { + return array( + 'desc' => '', + 'repeatable' => false, + 'sortable' => false, + 'repeatable_max' => null, + 'show_label' => false, + 'readonly' => false, + 'disabled' => false, + 'default' => '', + 'cols' => '12', + 'style' => '', + 'class' => '', + 'data_delegate' => null, + 'save_callback' => null, + 'capability' => 'edit_posts', + 'string-repeat-field' => __( 'Add New', 'cmb' ), + 'string-delete-field' => __( 'Remove', 'cmb' ), + 'confirm_delete' => true, + ); + } + + /** + * Get the default args for the abstract field. + * These args are available to all fields. + * + * @return array $args + */ + public function get_default_args() { + + /** + * Filter the default arguments passed by a field class. + * + * @param array $args default field arguments. + * @param string $class Field class being called + */ + return apply_filters( 'cmb_field_default_args', $this->default_args(), get_class( $this ) ); + } + + /** + * Enqueue all scripts required by the field. + * + * @uses wp_enqueue_script() + */ + public function enqueue_scripts() { + + if ( isset( $this->args['sortable'] ) && $this->args['sortable'] && $this->args['repeatable'] ) { + wp_enqueue_script( 'jquery-ui-sortable' ); + } + + } + + /** + * Enqueue all styles required by the field. + * + * @uses wp_enqueue_style() + */ + public function enqueue_styles() {} + + /** + * Output the field input ID attribute. + * + * If multiple inputs are required for a single field, + * use the append parameter to add unique identifier. + * + * @param string $append Optional. ID to place. + */ + public function id_attr( $append = null ) { + + /** + * Modify the id attribute of a field. + * + * @param string $id ID Attribute + * @param string $append ID to place. + * @param array $args Arguments for this particular field. + */ + $id = apply_filters( 'cmb_field_id_attribute', $this->get_the_id_attr( $append ), $append, $this->args ); + + printf( 'id="%s"', esc_attr( $id ) ); + + } + + /** + * Output the for attribute for the field. + * + * If multiple inputs are required for a single field, + * use the append parameter to add unique identifier. + * + * @param string $append Optional. ID to place. + * @return string Modified id attribute contents + */ + public function get_the_id_attr( $append = null ) { + + $id = $this->id; + + if ( isset( $this->parent ) ) { + $parent_id = preg_replace( '/cmb\-field\-(\d+|x)/', 'cmb-group-$1', $this->parent->get_the_id_attr() ); + $id = $parent_id . '[' . $id . ']'; + } + + $id .= '-cmb-field-' . $this->field_index; + + if ( ! is_null( $append ) ) { + $id .= '-' . $append; + } + + $id = str_replace( array( '[', ']', '--' ), '-', $id ); + + return $id; + + } + + /** + * Output the field input ID attribute value. + * + * If multiple inputs are required for a single field, + * use the append parameter to add unique identifier. + * + * @see get_the_id_attr + * + * @param string $append Optional. for value to place. + */ + public function for_attr( $append = null ) { + + /** + * Modify the for attribute of a field. + * + * @param string $for For attribute + * @param array $args Arguments for this particular field. + */ + $for = apply_filters( 'cmb_field_for_attribute', $this->get_the_id_attr( $append ), $this->args ); + + printf( 'for="%s"', esc_attr( $for ) ); + + } + + /** + * Output HTML name attribute for a field. + * + * @see get_the_name_attr + * + * @param string $append Optional. Name to place. + */ + public function name_attr( $append = null ) { + + /** + * Modify the name attribute of a field. + * + * @param string $name Name attribute + * @param array $args Arguments for this particular field. + */ + $name = apply_filters( 'cmb_field_name_attribute', $this->get_the_name_attr( $append ), $this->args ); + + printf( 'name="%s"', esc_attr( $name ) ); + + } + + /** + * Get the name attribute contents for a field. + * + * @param null $append Optional. Name to place. + * @return string Name attribute contents. + */ + public function get_the_name_attr( $append = null ) { + + $name = str_replace( '[]', '', $this->name ); + + if ( isset( $this->parent ) ) { + $parent_name = preg_replace( '/cmb\-field\-(\d+|x)/', 'cmb-group-$1', $this->parent->get_the_name_attr() ); + $name = $parent_name . '[' . $name . ']'; + } + + $name .= "[cmb-field-$this->field_index]"; + + if ( ! is_null( $append ) ) { + $name .= $append; + } + + return $name; + + } + + /** + * Output class attribute for a field. + * + * @param string $classes Optional. Classes to assign to the field. + */ + public function class_attr( $classes = '' ) { + + // Combine any passed-in classes and the ones defined in the arguments and sanitize them. + $all_classes = array_unique( explode( ' ', $classes . ' ' . $this->args['class'] ) ); + $classes = array_map( 'sanitize_html_class', array_filter( $all_classes ) ); + + /** + * Modify the classes assigned to a field. + * + * @param array $classes Classes currently assigned to the field + * @param array $args Arguments for this particular field. + */ + $classes = apply_filters( 'cmb_field_classes', $classes, $this->args ); + + if ( $classes = implode( ' ', $classes ) ) { ?> + + class="" + + get_the_id_attr() ); + + } + + /** + * Print one or more HTML5 attributes for a field. + * + * @param array $attrs Optional. Attributes to define in the field. + */ + public function boolean_attr( $attrs = array() ) { + + if ( $this->args['readonly'] ) { + $attrs[] = 'readonly'; + } + + if ( $this->args['disabled'] ) { + $attrs[] = 'disabled'; + } + + $attrs = array_filter( array_unique( $attrs ) ); + + /** + * Modify any boolean attributes assigned to a field. + * + * @param array $attrs Boolean attributes. + * @param array $args Arguments for this particular field. + */ + $attrs = apply_filters( 'cmb_field_boolean_attributes', $attrs, $this->args ); + + foreach ( $attrs as $attr ) { + echo esc_html( $attr ) . '="' . esc_attr( $attr ) . '"'; + } + + } + + /** + * Check if this field has a data delegate set + * + * @return boolean Set or turned off. + */ + public function has_data_delegate() { + return (bool) $this->args['data_delegate']; + } + + /** + * Get the array of data from the data delegate. + * + * @return array mixed + */ + protected function get_delegate_data() { + + if ( $this->args['data_delegate'] ) { + return call_user_func_array( $this->args['data_delegate'], array( $this ) ); + } + + return array(); + + } + + /** + * Get the existing or default value for a field. + * + * @return mixed + */ + public function get_value() { + return ( $this->value || '0' === $this->value ) ? $this->value : $this->args['default']; + } + + /** + * Get multiple values for a field. + * + * @return array + */ + public function get_values() { + return $this->values; + } + + /** + * Define multiple values for a field and completely remove the singular value variable. + * + * @param array $values Field values. + */ + public function set_values( array $values ) { + + $this->values = $values; + + unset( $this->value ); + + } + + /** + * Parse and validate an array of values. + * + * Meant to be extended. + */ + public function parse_save_values() {} + + /** + * Parse and validate a single value. + * + * Meant to be extended. + */ + public function parse_save_value() {} + + /** + * Save values for the field. + * + * @todo this surely only works for posts + * @todo why do values need to be passed in, they can already be passed in on construct + * + * @param int $post_id Post ID. + * @param array $values Values to save. + */ + public function save( $post_id, $values ) { + + // Don't save readonly values. + if ( $this->args['readonly'] ) { + return; + } + + $this->values = $values; + $this->parse_save_values(); + + // Allow override from args. + if ( ! empty( $this->args['save_callback'] ) ) { + + call_user_func( $this->args['save_callback'], $this->values, $post_id ); + + return; + + } + + // If we are not on a post edit screen. + if ( ! $post_id ) { + return; + } + + delete_post_meta( $post_id, $this->id ); + + foreach ( $this->values as $v ) { + + $this->value = $v; + $this->parse_save_value(); + + if ( $this->value || '0' === $this->value ) { + add_post_meta( $post_id, $this->id, $this->value ); + } + } + } + + /** + * Check whether the current field should or should not be displayed. + * + * @param int $post_id Post ID to check against. + */ + public function is_displayed( $post_id ) { + return current_user_can( $this->args['capability'], $post_id ); + } + + /** + * Print title for field. + */ + public function title() { + + if ( $this->title ) : ?> + +
+ +
+ + args['desc'] ) ) : ?> + +
+ args['desc'] ); ?> +
+ + get_values() && ! $this->args['repeatable'] ) { + $values = array( '' ); + } else { + $values = $this->get_values(); // Make PHP5.4 >= happy. + $values = empty( $values ) ? array( '' ) : $values; + } + + // Print title if necessary. + $this->title(); + + // Print description if necessary. + $this->description(); + + $i = 0; + + // If the map field is not repeatable then we need to wrap it within an + // array to remove difference in data structure between a repeater field and normal + if ( isset( $this->args['type'] ) && 'gmap' === $this->args['type'] ) { + if ( $this->args['repeatable'] === false && ! isset( $this->parent ) ) { + $values = array( $values ); + } + + // If its within a group then we need to unwrap it from the group, + if ( $this->args['repeatable'] === false && isset( $this->parent ) ) { + $values = $values[0]; + + // Transitions code for data formats see: + // https://github.com/humanmade/Custom-Meta-Boxes/issues/422 + if ( count( $values ) !== 1 ) { + $values = array( $values ); + } + } + } + + foreach ( $values as $key => $value ) { + + $this->field_index = $i; + $this->value = $value; + ?> + +
+ + args['repeatable'] ) { + $this->delete_button_markup(); + } ?> + + html(); ?> + +
+ + args['repeatable'] ) { + $this->repeatable_button_markup(); + } + } + + /** + * Markup to print an "add" button and for a repeatable field. + */ + protected function repeatable_button_markup() { + // X used to distinguish hidden fields. + $this->field_index = 'x'; + $this->value = ''; + ?> + + + + + + + + in_admin() ) { + return null; + } + + $id = $GLOBALS['hook_suffix']; + $id = ( '.php' == substr( $id, -4 ) ) ? substr( $id, 0, -4 ) : $id; + if ( 'post-new' === $id || 'link-add' === $id || 'media-new' === $id || 'user-new' === $id ) { + return true; + } + + return false; + } + + /** + * Setup arguments for the class. + * + * @param $arguments + */ + public function set_arguments( $arguments ) { + // Initially set arguments up. + $this->args = wp_parse_args( $arguments, $this->get_default_args() ); + + // Support the deprecated argument: 'std' + if ( ! empty( $this->args['std'] ) && empty( $this->args['default'] ) ) { + $this->args['default'] = $this->args['std']; + _deprecated_argument( 'CMB_Field', '0.9', "field argument 'std' is deprecated, use 'default' instead" ); + } + + if ( ! empty( $this->args['options'] ) && is_array( reset( $this->args['options'] ) ) ) { + $re_format = array(); + foreach ( $this->args['options'] as $option ) { + $re_format[ $option['value'] ] = $option['name']; + } + $this->args['options'] = $re_format; + } + } +} diff --git a/fields/class-cmb-file-field.php b/fields/class-cmb-file-field.php new file mode 100644 index 00000000..929f2e3b --- /dev/null +++ b/fields/class-cmb-file-field.php @@ -0,0 +1,112 @@ + array( + 'video', + 'audio', + 'text', + 'application', + ), + ) + ); + } + + /** + * Enqueue all scripts required by the field. + * + * @uses wp_enqueue_script() + */ + function enqueue_scripts() { + + global $post_ID; + $post_ID = isset( $post_ID ) ? (int) $post_ID : 0; + + parent::enqueue_scripts(); + + wp_enqueue_media( array( 'post' => $post_ID ) ); + wp_enqueue_script( 'cmb-file-upload', trailingslashit( CMB_URL ) . 'js/file-upload.js', array( 'jquery', 'cmb-scripts' ), CMB_VERSION ); + + } + + /** + * Print out field HTML. + */ + public function html() { + + if ( $this->get_value() ) { + $src = wp_mime_type_icon( $this->get_value() ); + if ( strpos( $src, site_url() !== false ) ) { + $size = getimagesize( str_replace( site_url(), ABSPATH, $src ) ); + } else { + $size = null; + } + $icon_img = ''; + } + + $data_type = ( ! empty( $this->args['library-type'] ) ? implode( ',', $this->args['library-type'] ) : null ); + + ?> + +
> + +
+ + + +
+ + get_value() ) : ?> + + + +
+ get_value() ) ) ); ?> +
+ + + +
+ + + + class_attr( 'cmb-file-upload-input' ); ?> + name_attr(); ?> + value="value ); ?>" + /> + +
+ + '100%', + 'field_height' => '250px', + 'default_lat' => '51.5073509', + 'default_long' => '-0.12775829999998223', + 'default_zoom' => '8', + 'string-marker-title' => esc_html__( 'Drag to set the exact location', 'cmb' ), + 'string-gmaps-api-not-loaded' => esc_html__( 'Google Maps API not loaded.', 'cmb' ), + 'google_api_key' => '', + 'language' => explode( '_', get_locale() )[0], + ) + ); + } + + /** + * Enqueue all scripts required by the field. + * + * @uses wp_enqueue_script() + */ + public function enqueue_scripts() { + + parent::enqueue_scripts(); + + wp_enqueue_script( 'cmb-google-maps-script', trailingslashit( CMB_URL ) . 'js/field-gmap.js', array( 'jquery' ), CMB_VERSION ); + + // Check for our key with either a field argument or constant. + $key = ''; + if ( ! empty( $this->args['google_api_key'] ) ) { + $key = $this->args['google_api_key']; + } elseif ( defined( 'CMB_GAPI_KEY' ) ) { + $key = CMB_GAPI_KEY; + } + + wp_localize_script( 'cmb-google-maps-script', 'CMBGmaps', array( + 'key' => $key, + 'defaults' => array( + 'latitude' => $this->args['default_lat'], + 'longitude' => $this->args['default_long'], + 'zoom' => $this->args['default_zoom'], + ), + 'strings' => array( + 'markerTitle' => $this->args['string-marker-title'], + 'googleMapsApiNotLoaded' => $this->args['string-gmaps-api-not-loaded'], + ), + 'language' => $this->args['language'], + ) ); + + } + + /** + * Get multiple values for a field. + * + * @return array + */ + public function get_values() { + return ( ! $this->args['repeatable'] ) ? array( $this->values ) : $this->values; + } + + /** + * Print out field HTML. + */ + public function html() { + + // Ensure all args used are set. + $value = wp_parse_args( + $this->get_value(), + array( + 'lat' => null, + 'long' => null, + 'elevation' => null, + 'text' => null, + ) + ); + + $style = array( + sprintf( 'width: %s;', $this->args['field_width'] ), + sprintf( 'height: %s;', $this->args['field_height'] ), + 'border: 1px solid #eee;', + 'margin-top: 8px;', + ); + + ?> + + class_attr( 'map-search' ); ?> id_attr(); ?> name_attr( '[text]' ); ?> value="" /> + +
+ + name_attr( '[lat]' ); ?> value="" /> + name_attr( '[long]' ); ?> value="" /> + name_attr( '[elevation]' ); ?> value="" /> + + args['fields'] ) ) { + foreach ( $this->args['fields'] as $f ) { + + $class = _cmb_field_class_for_type( $f['type'] ); + + if ( ! empty( $class ) && class_exists( $class ) ) { + $this->add_field( new $class( $f['id'], $f['name'], array(), $f ) ); + } + } + } + + } + + /** + * Get default arguments for field including custom parameters. + * + * @return array Default arguments for field. + */ + public function default_args() { + return array_merge( + parent::default_args(), + array( + 'fields' => array(), + 'string-repeat-field' => __( 'Add New Group', 'cmb' ), + 'string-delete-field' => __( 'Remove Group', 'cmb' ), + ) + ); + } + + /** + * Enqueue all scripts required by the field. + * + * @uses wp_enqueue_script() + */ + public function enqueue_scripts() { + + parent::enqueue_scripts(); + + foreach ( $this->args['fields'] as $f ) { + $class = _cmb_field_class_for_type( $f['type'] ); + if ( ! empty( $class ) && class_exists( $class ) ) { + $field = new $class( '', '', array(), $f ); + $field->enqueue_scripts(); + } + } + + } + + /** + * Enqueue all styles required by the field. + * + * @uses wp_enqueue_style() + */ + public function enqueue_styles() { + + parent::enqueue_styles(); + + foreach ( $this->args['fields'] as $f ) { + $class = _cmb_field_class_for_type( $f['type'] ); + if ( ! empty( $class ) && class_exists( $class ) ) { + $field = new $class( '', '', array(), $f ); + $field->enqueue_styles(); + } + } + + } + + /** + * Display output for group. + */ + public function display() { + + global $post; + + $field = $this->args; + $values = $this->get_values(); + + $this->title(); + $this->description(); + + if ( ! $this->args['repeatable'] && empty( $values ) ) { + $values = array( null ); + } else { + $values = $this->get_values(); // Make PHP5.4 >= happy. + $values = ( empty( $values ) ) ? array( '' ) : $values; + } + + $i = 0; + foreach ( $values as $value ) { + + $this->field_index = $i; + $this->value = $value; + + ?> + +
+ html(); ?> +
+ + args['repeatable'] ) { + + $this->field_index = 'x'; // X used to distinguish hidden fields. + $this->value = ''; + + ?> + + + + + + get_fields(); + $value = $this->get_value(); + + // Reset all field values. + foreach ( $fields as $field ) { + $field->set_values( array() ); + } + + // Set values for this field. + if ( ! empty( $value ) ) { + foreach ( $value as $field_id => $field_value ) { + $field_value = ( ! empty( $field_value ) ) ? $field_value : array(); + if ( ! empty( $fields[ $field_id ] ) ) { + $fields[ $field_id ]->set_values( (array) $field_value ); + } + } + } + + ?> + + args['repeatable'] ) : ?> + + + + + + get_fields(); + $values = $this->get_values(); + + foreach ( $values as &$group_value ) { + foreach ( $group_value as $field_id => &$field_value ) { + + if ( ! isset( $fields[ $field_id ] ) ) { + $field_value = array(); + continue; + } + + $field = $fields[ $field_id ]; + $field->set_values( $field_value ); + $field->parse_save_values(); + + $field_value = $field->get_values(); + + // if the field is a repeatable field, store the whole array of them, if it's not repeatble, + // just store the first (and only) one directly. + if ( ! $field->args['repeatable'] ) { + $field_value = reset( $field_value ); + } + } + } + + $this->set_values( $values ); + } + + /** + * Add assigned fields to group data. + * + * @param CMB_Field $field Field object. + */ + public function add_field( CMB_Field $field ) { + $field->parent = $this; + $this->fields[ $field->id ] = $field; + } + + /** + * Assemble all defined fields for group. + * + * @return array + */ + public function get_fields() { + return $this->fields; + } + + /** + * Set values for each field in the group. + * + * @param array $values Existing or default values for all fields. + */ + public function set_values( array $values ) { + + $fields = $this->get_fields(); + $this->values = $values; + + // Reset all field values. + foreach ( $fields as $field ) { + $field->set_values( array() ); + } + + foreach ( $values as $value ) { + foreach ( $value as $field_id => $field_value ) { + $fields[ $field_id ]->set_values( (array) $field_value ); + } + } + + } +} diff --git a/fields/class-cmb-hidden-field.php b/fields/class-cmb-hidden-field.php new file mode 100644 index 00000000..b0cc10fb --- /dev/null +++ b/fields/class-cmb-hidden-field.php @@ -0,0 +1,43 @@ + + + + + + + id_attr(); ?> boolean_attr(); ?> class_attr(); ?> name_attr(); ?> value="get_value() ); ?>" /> + + 'thumbnail', + 'library-type' => array( + 'image', + ), + 'show_size' => false, + ) + ); + } + + /** + * Print out field HTML. + */ + public function html() { + + if ( $this->get_value() ) { + $image = wp_get_attachment_image_src( $this->get_value(), $this->args['size'], true ); + } + + // Convert size arg to array of width, height, crop. + $size = $this->parse_image_size( $this->args['size'] ); + + // Inline styles. + $styles = sprintf( 'width: %1$dpx; height: %2$dpx; line-height: %2$dpx', intval( $size['width'] ), intval( $size['height'] ) ); + $placeholder_styles = sprintf( 'width: %dpx; height: %dpx;', intval( $size['width'] ) - 8, intval( $size['height'] ) - 8 ); + + $data_type = ( ! empty( $this->args['library-type'] ) ? implode( ',', $this->args['library-type'] ) : null ); + + ?> + +
+ +
+ + args['show_size'] ) : ?> + + + + + +
+ + + +
+ + + + + +
+ + + + class_attr( 'cmb-file-upload-input' ); ?> + name_attr(); ?> + value="value ); ?>" + /> + +
+ + get_option( $size . '_size_w' ), + 'height' => get_option( $size . '_size_h' ), + 'crop' => get_option( $size . '_crop' ), + ); + } + + // Handle string for additional image sizes. + global $_wp_additional_image_sizes; + if ( is_string( $size ) && isset( $_wp_additional_image_sizes[ $size ] ) ) { + return array( + 'width' => $_wp_additional_image_sizes[ $size ]['width'], + 'height' => $_wp_additional_image_sizes[ $size ]['height'], + 'crop' => $_wp_additional_image_sizes[ $size ]['crop'], + ); + } + + // Handle default WP size format. + if ( is_array( $size ) && isset( $size[0] ) && isset( $size[1] ) ) { + $size = array( 'width' => $size[0], 'height' => $size[1] ); + } + + return wp_parse_args( $size, array( + 'width' => get_option( 'thumbnail_size_w' ), + 'height' => get_option( 'thumbnail_size_h' ), + 'crop' => get_option( 'thumbnail_crop' ), + ) ); + + } + + /** + * Ajax callback for outputing an image src based on post data. + * + * @return null + */ + static function request_image_ajax_callback() { + + if ( ! ( isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], 'cmb-file-upload-nonce' ) ) ) { + return; + } + + $id = absint( $_POST['id'] ); + + $size = array( + intval( $_POST['width'] ), + intval( $_POST['height'] ), + 'crop' => (bool) $_POST['crop'], + ); + + $image = wp_get_attachment_image_src( $id, $size ); + echo esc_url( reset( $image ) ); + + // This is required to return a proper result. + die(); + } +} + +// @todo:: this has got to go somewhere else. +add_action( 'wp_ajax_cmb_request_image', array( 'CMB_Image_Field', 'request_image_ajax_callback' ) ); diff --git a/fields/class-cmb-number-field.php b/fields/class-cmb-number-field.php new file mode 100644 index 00000000..b4c3123c --- /dev/null +++ b/fields/class-cmb-number-field.php @@ -0,0 +1,45 @@ + '', + 'min' => '', + 'max' => '', + ) + ); + } + + /** + * Print out field HTML. + */ + public function html() { + $attrs = []; + $attrs[] = '' !== $this->args['step'] ? sprintf( 'step="%g"', $this->args['step'] ) : ''; + $attrs[] = '' !== $this->args['min'] ? sprintf( 'min="%g"', $this->args['min'] ) : ''; + $attrs[] = '' !== $this->args['max'] ? sprintf( 'max="%g"', $this->args['max'] ) : ''; + ?> + + type="number" id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_text_number code' ); ?> name_attr(); ?> value="get_value() ); ?>" /> + + array Array of options to show in the select, optionally use data_delegate instead + * 'allow_none' => bool Allow no option to be selected (will place a "None" at the top of the select) + * 'multiple' => bool whether multiple can be selected + * + * @since 1.0.0 + * + * @extends CMB_Select + * + * @package WordPress + * @subpackage Custom Meta Boxes + */ + +class CMB_Post_Select extends CMB_Select { + + /** + * CMB_Post_Select constructor. + */ + public function __construct() { + + $args = func_get_args(); + + call_user_func_array( array( 'parent', '__construct' ), $args ); + + if ( ! $this->args['use_ajax'] ) { + + $this->args['data_delegate'] = array( $this, 'get_delegate_data' ); + + } + + } + + /** + * Get default arguments for field including custom parameters. + * + * @return array Default arguments for field. + */ + public function default_args() { + return array_merge( + parent::default_args(), + array( + 'query' => array(), + 'use_ajax' => false, + 'multiple' => false, + ) + ); + } + + /** + * Get posts and verify for use in select field. + * + * @todo:: validate this data before returning. + * + * @return array Array of posts for field. + */ + public function get_delegate_data() { + + $data = array(); + + foreach ( $this->get_posts() as $post_id ) { + $data[ $post_id ] = get_the_title( $post_id ); + } + + return $data; + + } + + /** + * Get posts for use in select field. + * + * @return array + */ + private function get_posts() { + + $this->args['query']['fields'] = 'ids'; + $query = new WP_Query( $this->args['query'] ); + + return isset( $query->posts ) ? $query->posts : array(); + + } + + /** + * Format the field values for Select2 to read. + */ + public function parse_save_value() { + + // AJAX multi select2 data is submitted as a string of comma separated post IDs. + // If empty, set to false instead of empty array to ensure the meta entry is deleted. + if ( $this->args['use_ajax'] && $this->args['multiple'] ) { + $this->value = ( ! empty( $this->value ) ) ? explode( ',', $this->value ) : false; + } + + } + + /** + * Assemble and output of field HTML. + */ + public function output_field() { + + // If AJAX, must use input type not standard select. + if ( $this->args['use_ajax'] ) : + + ?> + + id_attr(); ?> + value ) ) ); ?> + get_the_name_attr() ) ); ?> + get_js_id() ) ); ?> + boolean_attr(); ?> + class="cmb_select" + style="width: 100%" + /> + + + + + + array(), + ) + ); + } + + /** + * Print out field HTML. + */ + public function html() { + + if ( $this->has_data_delegate() ) { + $this->args['options'] = $this->get_delegate_data(); + } ?> + + args['options'] as $key => $value ) : ?> + + id_attr( 'item-' . $key ); ?> boolean_attr(); ?> class_attr(); ?> type="radio" name_attr(); ?> value="" get_value() ); ?> /> + + + + + array Array of options to show in the select, optionally use data_delegate instead + * 'allow_none' => bool|string Allow no option to be selected (will place a "None" at the top of the select) + * 'multiple' => bool whether multiple can be selected + * + * @since 1.0.0 + * + * @extends CMB_Field + * + * @package WordPress + * @subpackage Custom Meta Boxes + */ + +class CMB_Select extends CMB_Field { + + /** + * CMB_Select constructor. + */ + public function __construct() { + + $args = func_get_args(); + + call_user_func_array( array( 'parent', '__construct' ), $args ); + + } + + /** + * Get default arguments for field including custom parameters. + * + * @return array Default arguments for field. + */ + public function default_args() { + return array_merge( + parent::default_args(), + array( + 'options' => array(), + 'multiple' => false, + 'select2_options' => array(), + 'allow_none' => false, + ) + ); + } + + /** + * Ensure values are saved as an array if multiple is set. + */ + public function parse_save_values() { + + if ( isset( $this->parent ) && isset( $this->args['multiple'] ) && $this->args['multiple'] ) { + $this->values = array( $this->values ); + } + + } + + /** + * Get options for field. + * + * @return mixed + */ + public function get_options() { + + if ( $this->has_data_delegate() ) { + $this->args['options'] = $this->get_delegate_data(); + } + + return $this->args['options']; + } + + /** + * Enqueue all scripts required by the field. + * + * @uses wp_enqueue_script() + */ + public function enqueue_scripts() { + + parent::enqueue_scripts(); + + wp_enqueue_script( 'select2', trailingslashit( CMB_URL ) . 'js/vendor/select2/select2.js', array( 'jquery' ) ); + wp_enqueue_script( 'field-select', trailingslashit( CMB_URL ) . 'js/field.select.js', array( 'jquery', 'select2', 'cmb-scripts' ), CMB_VERSION ); + } + + /** + * Enqueue all styles required by the field. + * + * @uses wp_enqueue_style() + */ + public function enqueue_styles() { + + parent::enqueue_styles(); + + wp_enqueue_style( 'select2', trailingslashit( CMB_URL ) . 'js/vendor/select2/select2.css' ); + } + + /** + * Print out field HTML. + */ + public function html() { + + if ( $this->has_data_delegate() ) { + $this->args['options'] = $this->get_delegate_data(); + } + + $this->output_field(); + + $this->output_script(); + + } + + /** + * Compile field HTML. + */ + public function output_field() { + + $val = (array) $this->get_value(); + + $name = $this->get_the_name_attr(); + $name .= ! empty( $this->args['multiple'] ) ? '[]' : null; + + $none = is_string( $this->args['allow_none'] ) ? $this->args['allow_none'] : __( 'None', 'cmb' ); + + ?> + + + + args['select2_options'], array( + 'placeholder' => __( 'Type to search', 'cmb' ), + 'allowClear' => true, + ) ); + + ?> + + + + '', + 'hide_empty' => false, + ) + ); + } + + /** + * CMB_Taxonomy constructor. + */ + public function __construct() { + + $args = func_get_args(); + + call_user_func_array( array( 'parent', '__construct' ), $args ); + + $this->args['data_delegate'] = array( $this, 'get_delegate_data' ); + + } + + /** + * Retrieve custom field data. + * + * @return array Terms for field data. + */ + public function get_delegate_data() { + + $terms = $this->get_terms(); + + if ( is_wp_error( $terms ) ) { + return array(); + } + + $term_options = array(); + + foreach ( $terms as $term ) { + $term_options[ $term->term_id ] = $term->name; + } + + return $term_options; + + } + + /** + * Get terms for select field. + * + * @todo::cache this or find a cached method + * + * @return array|int|WP_Error + */ + private function get_terms() { + + return get_terms( $this->args['taxonomy'], array( 'hide_empty' => $this->args['hide_empty'] ) ); + + } +} diff --git a/fields/class-cmb-text-field.php b/fields/class-cmb-text-field.php new file mode 100644 index 00000000..211e416d --- /dev/null +++ b/fields/class-cmb-text-field.php @@ -0,0 +1,25 @@ + + + id_attr(); ?> boolean_attr(); ?> class_attr(); ?> name_attr(); ?> value="get_value() ); ?>" /> + + args['class'] .= ' cmb_text_small'; + + parent::html(); + + } +} diff --git a/fields/class-cmb-textarea-field-code.php b/fields/class-cmb-textarea-field-code.php new file mode 100644 index 00000000..132808d0 --- /dev/null +++ b/fields/class-cmb-textarea-field-code.php @@ -0,0 +1,28 @@ + + * + * @since 1.0.0 + * + * @extends CMB_Textarea_Field + * + * @package WordPress + * @subpackage Custom Meta Boxes + */ + +class CMB_Textarea_Field_Code extends CMB_Textarea_Field { + + /** + * Print out field HTML. + */ + public function html() { + + $this->args['class'] .= ' code'; + + parent::html(); + + } +} diff --git a/fields/class-cmb-textarea-field.php b/fields/class-cmb-textarea-field.php new file mode 100644 index 00000000..f7f891ae --- /dev/null +++ b/fields/class-cmb-textarea-field.php @@ -0,0 +1,28 @@ + + * + * @since 1.0.0 + * + * @extends CMB_Field + * + * @package WordPress + * @subpackage Custom Meta Boxes + */ + +class CMB_Textarea_Field extends CMB_Field { + + /** + * Print out field HTML. + */ + public function html() { + ?> + + + + + + id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_text_small cmb_timepicker' ); ?> type="text" name_attr(); ?> value="value ); ?>"/> + + + +
+

class_attr(); ?>> + title ); ?> +

+
+ + + + id_attr(); ?> boolean_attr(); ?> class_attr( 'cmb_text_url code' ); ?> name_attr(); ?> value="value ) ); ?>" /> + + array(), + ) + ); + } + + /** + * Enqueue all scripts required by the field. + * + * @uses wp_enqueue_script() + */ + function enqueue_scripts() { + + parent::enqueue_scripts(); + + wp_enqueue_script( 'cmb-wysiwyg', trailingslashit( CMB_URL ) . 'js/field-wysiwyg.js', array( 'jquery', 'cmb-scripts' ), CMB_VERSION ); + } + + /** + * Print out field HTML. + */ + public function html() { + + $id = $this->get_the_id_attr(); + $name = $this->get_the_name_attr(); + + $field_id = $this->get_js_id(); + + printf( + '
', + esc_attr( $id ), + esc_attr( $name ), + esc_attr( $field_id ) + ); + + if ( $this->is_placeholder() ) { + + // For placeholder, output the markup for the editor in a JS var. + ob_start(); + $this->args['options']['textarea_name'] = 'cmb-placeholder-name-' . $field_id; + wp_editor( '', 'cmb-placeholder-id-' . $field_id, $this->args['options'] ); + $editor = ob_get_clean(); + $editor = str_replace( array( "\n", "\r" ), '', $editor ); + $editor = str_replace( array( "'" ), '"', $editor ); + + ?> + + + + args['options']['textarea_name'] = $name; + wp_editor( $this->get_value(), $id, $this->args['options'] ); + + } + + echo '
'; + + } + + /** + * Check if this is a placeholder field. + * Either the field itself, or because it is part of a repeatable group. + * + * @return bool + */ + public function is_placeholder() { + + if ( isset( $this->parent ) && ! is_int( $this->parent->field_index ) ) { + return true; + } else { + return ! is_int( $this->field_index ); + } + + } +} diff --git a/js/cmb.js b/js/cmb.js index 1c7c761d..7c9a664a 100644 --- a/js/cmb.js +++ b/js/cmb.js @@ -101,7 +101,10 @@ var CMB = { e.preventDefault(); jQuery( this ).blur(); - if ( ! confirm( CMBData.strings.confirmDeleteField ) ) { + fieldItem = jQuery( this ).closest( '.field-item' ); + field = fieldItem.closest( '.field' ); + + if ( false !== field.data( 'confirm-delete' ) && ! confirm( CMBData.strings.confirmDeleteField ) ) { return; } @@ -325,6 +328,34 @@ var CMB = { this._sortEndCallbacks[fieldName] = this._sortEndCallbacks[fieldName] ? this._sortEndCallbacks[fieldName] : []; this._sortEndCallbacks[fieldName].push( callback ); + }, + + /** + * Simple debouncing function. + * + * @param func func function to run after wait period. + * @param wait int Wait interval. + * @param immediate bool Whether to run immediately or not. + * @returns {Function} + */ + debounce: function debounce( func, wait, immediate ) { + var timeout; + return function() { + var context = this, + args = arguments; + var later = function() { + timeout = null; + if ( ! immediate ) { + func.apply( context, args ); + } + }; + var callNow = immediate && ! timeout; + clearTimeout( timeout ); + timeout = setTimeout( later, wait ); + if ( callNow ) { + func.apply( context, args ); + } + }; } }; diff --git a/js/field-gmap.js b/js/field-gmap.js index 30e8dade..b8baad3f 100644 --- a/js/field-gmap.js +++ b/js/field-gmap.js @@ -100,6 +100,6 @@ CMB.addCallbackForClonedField( ['CMB_Gmap_Field'], CMBGmapsInit ); }; - $.getScript( '//maps.google.com/maps/api/js?sensor=true&libraries=places&callback=CMB_CMAPS_INIT&key=' + CMBGmaps.key ); + $.getScript( '//maps.google.com/maps/api/js?sensor=true&libraries=places&language=' + CMBGmaps.language + '&callback=CMB_CMAPS_INIT&key=' + CMBGmaps.key ); }(jQuery)); diff --git a/js/field-wysiwyg.js b/js/field-wysiwyg.js index b7640210..a1c32ea8 100644 --- a/js/field-wysiwyg.js +++ b/js/field-wysiwyg.js @@ -63,16 +63,17 @@ CMB.addCallbackForClonedField( 'CMB_wysiwyg', function( newT ) { var ed = tinymce.init( tinyMCEPreInit.mceInit[id] ); } else if ( tinyMCE.majorVersion === '3' ) { var ed = new tinymce.Editor( id, tinyMCEPreInit.mceInit[id] ); + ed.render(); } - ed.render(); } - // Init Quicktags. - QTags.instances[0] = undefined; - try { - quicktags( tinyMCEPreInit.qtInit[ id ] ); - } catch ( e ) {} - + // Init Quicktags, only if we have have QuickTags specified. + if ( ! jQuery.isEmptyObject( newQTS ) ) { + QTags.instances[ 0 ] = undefined; + try { + quicktags( tinyMCEPreInit.qtInit[ id ] ); + } catch ( e ) {} + } } ); } ); diff --git a/js/file-upload.js b/js/file-upload.js index ebd07886..14a81881 100644 --- a/js/file-upload.js +++ b/js/file-upload.js @@ -99,8 +99,7 @@ jQuery( document ).ready( function() { var el = jQuery( this ), container = el.closest( '.postbox' ), - width = container.width() - 12 - 10 - 10, - ratio = el.height() / el.width(); + width = container.width() - 12 - 10 - 10 - 2; if ( el.attr( 'data-original-width' ) ) { el.width( el.attr( 'data-original-width' ) ); @@ -114,18 +113,30 @@ jQuery( document ).ready( function() { el.attr( 'data-original-height', el.height() ); } - if ( el.width() > width ) { - el.width( width ); - el.find( '.cmb-file-wrap-placeholder' ).width( width - 8 ); - el.height( width * ratio ); - el.css( 'line-height', ( width * ratio ) + 'px' ); - el.find( '.cmb-file-wrap-placeholder' ).height( ( width * ratio ) - 8 ); + var ratio = el.height() / el.width(); + + if ( el.width() > width || ( width > el.width() && width <= el.data( 'max-width' ) ) ) { + resizeFileBox( el, width, ratio ); } } ); + + function resizeFileBox( element, width, ratio ) { + element.width( width ); + element.find( '.cmb-file-wrap-placeholder' ).width( width - 8 ); + element.height( width * ratio ); + element.css( 'line-height', ( width * ratio ) + 'px' ); + element.find( '.cmb-file-wrap-placeholder' ).height( ( width * ratio ) - 8 ); + } }; recalculateFileFieldSize(); - jQuery( window ).resize( recalculateFileFieldSize ); + + var debounce = CMB.debounce( function() { + recalculateFileFieldSize(); + }, 100 ); + + // Recheck the width every time that the window is re-sized, debounced. + window.addEventListener( 'resize', debounce ); } ); diff --git a/languages/cmb-de_DE.mo b/languages/cmb-de_DE.mo new file mode 100644 index 00000000..07460485 Binary files /dev/null and b/languages/cmb-de_DE.mo differ diff --git a/languages/cmb-de_DE.po b/languages/cmb-de_DE.po new file mode 100644 index 00000000..b10e2b2f --- /dev/null +++ b/languages/cmb-de_DE.po @@ -0,0 +1,104 @@ +msgid "" +msgstr "" +"Project-Id-Version: Custom Meta Boxes\n" +"POT-Creation-Date: 2017-02-07 11:11+0100\n" +"PO-Revision-Date: 2017-02-07 11:12+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: de_DE\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.11\n" +"X-Poedit-Basepath: ..\n" +"X-Poedit-WPHeader: custom-meta-boxes.php\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;" +"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;" +"_nx_noop:3c,1,2;__ngettext_noop:1,2\n" +"X-Poedit-SearchPath-0: .\n" +"X-Poedit-SearchPathExcluded-0: *.js\n" + +#: class.cmb-meta-box.php:163 +msgid "Are you sure you want to delete this field?" +msgstr "Sind Sie sicher, dass Sie dieses Feld löschen wollen?" + +#: class.cmb-meta-box.php:455 +msgid "" +"Calling sortable on a non-repeatable field. A field cannot be sortable " +"without being repeatable." +msgstr "" +"Das \"sortable\" Attribut ist ein nicht-wiederholbares Feld zugewiesen. Ein " +"Feld kann nicht \"sortable\" sein wenn es nicht \"repeatable\" ist." + +#: classes.fields.php:92 +msgid "Add New" +msgstr "Neu hinzufügen" + +#: classes.fields.php:93 classes.fields.php:729 classes.fields.php:816 +msgid "Remove" +msgstr "Entfernen" + +#: classes.fields.php:707 +msgid "Add File" +msgstr "Datei hinzufügen" + +#: classes.fields.php:804 +msgid "Add Image" +msgstr "Bild hinzufügen" + +#: classes.fields.php:1539 +msgid "None" +msgstr "Keine" + +#: classes.fields.php:1572 +msgid "Type to search" +msgstr "Suchbegriff" + +#: classes.fields.php:1970 +msgid "Add New Group" +msgstr "Neue Gruppe hinzufügen" + +#: classes.fields.php:1971 +msgid "Remove Group" +msgstr "Gruppe löschen" + +#: example-functions.php:42 +msgid "Name" +msgstr "Name" + +#: example-functions.php:45 +msgid "Repeatable" +msgstr "Wiederholbar" + +#: example-functions.php:200 fields/class-cmb-gmap-field.php:37 +msgid "Drag to set the exact location" +msgstr "Platziere die Markierung, um den genauen Standort fest zu legen" + +#: example-functions.php:203 fields/class-cmb-gmap-field.php:38 +msgid "Google Maps API not loaded." +msgstr "Google Maps API nicht geladen." + +#. Plugin Name of the plugin/theme +msgid "Custom Meta Boxes" +msgstr "" + +#. Plugin URI of the plugin/theme +msgid "https://github.com/humanmade/Custom-Meta-Boxes" +msgstr "" + +#. Description of the plugin/theme +msgid "" +"Lets you easily create metaboxes with custom fields that will blow your " +"mind. Originally a fork of https://github.com/jaredatch/Custom-Metaboxes-and-" +"Fields-for-WordPress." +msgstr "" + +#. Author of the plugin/theme +msgid "Human Made Limited" +msgstr "" + +#. Author URI of the plugin/theme +msgid "http://hmn.md" +msgstr "" diff --git a/languages/cmb-hi_IN.mo b/languages/cmb-hi_IN.mo new file mode 100644 index 00000000..611dab98 Binary files /dev/null and b/languages/cmb-hi_IN.mo differ diff --git a/languages/cmb-hi_IN.po b/languages/cmb-hi_IN.po new file mode 100644 index 00000000..167bec6b --- /dev/null +++ b/languages/cmb-hi_IN.po @@ -0,0 +1,90 @@ +msgid "" +msgstr "" +"Project-Id-Version: Custom Meta Boxes\n" +"POT-Creation-Date: 2017-01-12 23:28+0530\n" +"PO-Revision-Date: 2017-01-12 23:53+0530\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: hi_IN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.7\n" +"X-Poedit-Basepath: ..\n" +"X-Poedit-WPHeader: custom-meta-boxes.php\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;" +"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;" +"_nx_noop:3c,1,2;__ngettext_noop:1,2\n" +"X-Poedit-SearchPath-0: .\n" +"X-Poedit-SearchPathExcluded-0: *.js\n" + +#: class.cmb-meta-box.php:144 +msgid "Are you sure you want to delete this field?" +msgstr "आप इस फील्ड को नष्ट करना चाहते हैं आप सुनिश्चित हैं?" + +#: classes.fields.php:94 +msgid "Add New" +msgstr "नया जोड़ें" + +#: classes.fields.php:95 classes.fields.php:622 classes.fields.php:709 +msgid "Remove" +msgstr "हटाना" + +#: classes.fields.php:600 +msgid "Add File" +msgstr "फाइल जोडें" + +#: classes.fields.php:697 +msgid "Add Image" +msgstr "छवि जोड़ें" + +#: classes.fields.php:1433 +msgid "None" +msgstr "कुछ नहीं " + +#: classes.fields.php:1466 +msgid "Type to search" +msgstr "खोजने के लिए लिखें" + +#: classes.fields.php:1862 +msgid "Add New Group" +msgstr "नया समूह जोड़ें" + +#: classes.fields.php:1863 +msgid "Remove Group" +msgstr "समूह को निकालें" + +#: classes.fields.php:2099 +msgid "Drag to set the exact location" +msgstr "खींचें सटीक स्थान निर्धारित करने के लिए" + +#: classes.fields.php:2100 +msgid "Google Maps API not loaded." +msgstr "गूगल मैप्स एपीआई लोड नहीं।" + +#. Plugin Name of the plugin/theme +msgid "Custom Meta Boxes" +msgstr "कस्टम मेटा बक्से" + +#. Plugin URI of the plugin/theme +msgid "https://github.com/humanmade/Custom-Meta-Boxes" +msgstr "https://github.com/humanmade/Custom-Meta-Boxes" + +#. Description of the plugin/theme +msgid "" +"Lets you easily create metaboxes with custom fields that will blow your " +"mind. Originally a fork of https://github.com/jaredatch/Custom-Metaboxes-and-" +"Fields-for-WordPress." +msgstr "" +"आप आसानी से कस्टम मेट बॉक्सेस और फ़ील्ड्स बना सकते हैं। मूलतः https://github.com/" +"jaredatch/Custom-Metaboxes-and-Fields-for-WordPress" + +#. Author of the plugin/theme +msgid "Human Made Limited" +msgstr "ह्यूमन मेड लिमिटेड " + +#. Author URI of the plugin/theme +msgid "http://hmn.md" +msgstr "http://hmn.md" diff --git a/languages/cmb-nl_NL.mo b/languages/cmb-nl_NL.mo new file mode 100644 index 00000000..c385b83e Binary files /dev/null and b/languages/cmb-nl_NL.mo differ diff --git a/languages/cmb-nl_NL.po b/languages/cmb-nl_NL.po new file mode 100644 index 00000000..8c052f32 --- /dev/null +++ b/languages/cmb-nl_NL.po @@ -0,0 +1,104 @@ +msgid "" +msgstr "" +"Project-Id-Version: Custom Meta Boxes\n" +"POT-Creation-Date: 2017-02-07 11:12+0100\n" +"PO-Revision-Date: 2017-02-07 11:15+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: nl_NL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.11\n" +"X-Poedit-Basepath: ..\n" +"X-Poedit-WPHeader: custom-meta-boxes.php\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;" +"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;" +"_nx_noop:3c,1,2;__ngettext_noop:1,2\n" +"X-Poedit-SearchPath-0: .\n" +"X-Poedit-SearchPathExcluded-0: *.js\n" + +#: class.cmb-meta-box.php:163 +msgid "Are you sure you want to delete this field?" +msgstr "Weet je zeker dat je dit veld wilt wissen?" + +#: class.cmb-meta-box.php:455 +msgid "" +"Calling sortable on a non-repeatable field. A field cannot be sortable " +"without being repeatable." +msgstr "" +"Sortable op een niet-herhaalbare veld opgeroepen. Een veld kan niet " +"sorteerbaar zijn zonder dat het herhaalbaar is." + +#: classes.fields.php:92 +msgid "Add New" +msgstr "Toevoegen" + +#: classes.fields.php:93 classes.fields.php:729 classes.fields.php:816 +msgid "Remove" +msgstr "Verwijder" + +#: classes.fields.php:707 +msgid "Add File" +msgstr "Bestand Toevoegen" + +#: classes.fields.php:804 +msgid "Add Image" +msgstr "Afbeelding toevoegen" + +#: classes.fields.php:1539 +msgid "None" +msgstr "Geen" + +#: classes.fields.php:1572 +msgid "Type to search" +msgstr "Type hier om te zoeken" + +#: classes.fields.php:1970 +msgid "Add New Group" +msgstr "Nieuwe groep toevoegen" + +#: classes.fields.php:1971 +msgid "Remove Group" +msgstr "Verwijder groep" + +#: example-functions.php:42 +msgid "Name" +msgstr "Naam" + +#: example-functions.php:45 +msgid "Repeatable" +msgstr "Herhaalbaar" + +#: example-functions.php:200 fields/class-cmb-gmap-field.php:37 +msgid "Drag to set the exact location" +msgstr "Sleep de marker om de exacte locatie in te stellen" + +#: example-functions.php:203 fields/class-cmb-gmap-field.php:38 +msgid "Google Maps API not loaded." +msgstr "Google Maps API niet geladen." + +#. Plugin Name of the plugin/theme +msgid "Custom Meta Boxes" +msgstr "" + +#. Plugin URI of the plugin/theme +msgid "https://github.com/humanmade/Custom-Meta-Boxes" +msgstr "" + +#. Description of the plugin/theme +msgid "" +"Lets you easily create metaboxes with custom fields that will blow your " +"mind. Originally a fork of https://github.com/jaredatch/Custom-Metaboxes-and-" +"Fields-for-WordPress." +msgstr "" + +#. Author of the plugin/theme +msgid "Human Made Limited" +msgstr "" + +#. Author URI of the plugin/theme +msgid "http://hmn.md" +msgstr "" diff --git a/package.json b/package.json index a560acb7..7454a104 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Custom-Meta-Boxes", - "version": "1.0.1", + "version": "1.1.0", "description": "Lets you easily create metaboxes with custom fields that will blow your mind.", "homepage": "https://github.com/humanmade/Custom-Meta-Boxes/", "repository": { diff --git a/readme.md b/readme.md index 20cbdafe..abc4d846 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ HM Custom Meta Boxes for WordPress
- A framework for easily adding custom fields to the WordPress post edit page + A framework for easily adding custom fields to the WordPress post edit page @@ -46,6 +46,10 @@ This plugin is maintained by [Human Made Limited](http://hmn.md) It began as a fork of [Custom Meta Boxes](https://github.com/jaredatch/Custom-Metaboxes-and-Fields-for-WordPress), but is no longer compatible. +## Minimum Requirements: +* PHP >= 5.4 +* WP >= 4.1 + ## Known Issues * Some fields do not work well as repeatable fields. * Some fields do not work well in repeatable groups. @@ -56,6 +60,25 @@ See [CONTRIBUTING.md](https://github.com/humanmade/Custom-Meta-Boxes/blob/master ## Changelog ## +**1.1** + +_Enhancements_ + - Added group field filter + - Cleaned up file upload styles + - Added Hindi translation (props @ajitbohra) + - Move all field classes to their own files + - Add min/max attributes to number input (props @shadvb) + - Use site language with Google Maps field (props @barryceelen) + +_Bug Fixes_ + - Filter all arguments, not just select ones + - Only attempt to call getimagesize() if the icon is local (props @joehoyle) + - Add Dutch and German translations (props @barryceelen) + - Align the file button vertically (props @ocean90) + - Fix for multiple wysiwyg fields not displaying in groups (props @tareiking) + - Fix incorrect gmap grouped field structure (props @dan-westall) + - Fix enqueuing of cmb-scripts (props @barryceelen) + **1.0.3** * Fix repeatable fields bugs (props @barryceelen ) * Fix gmaps field bug where key doesn't pass in correctly (props: @shadyvb ) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9d9bcebf..4d56e165 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,5 +1,19 @@ getMethod( $method_name ); + $method->setAccessible( true ); + + return $method->invokeArgs( $object, $parameters ); +} \ No newline at end of file diff --git a/tests/class-test-field-case.php b/tests/class-test-field-case.php new file mode 100644 index 00000000..66f1e556 --- /dev/null +++ b/tests/class-test-field-case.php @@ -0,0 +1,190 @@ + 1, + 'post_status' => 'publish', + 'post_content' => rand_str(), + 'post_title' => rand_str(), + 'post_type' => 'post', + ]; + + // Setup a default post for usage within test. + $factory = new WP_UnitTest_Factory(); + self::$post = $factory->post->create_and_get( $post_args ); + + // Capture WP Scripts object before usage. + self::$old_wp_scripts = isset( $GLOBALS['wp_scripts'] ) ? $GLOBALS['wp_scripts'] : null; + } + + /** + * Clean up fixtures. + */ + public static function tearDownAfterClass() { + parent::tearDownAfterClass(); + + wp_delete_post( self::$post->ID ); + $GLOBALS['wp_scripts'] = self::$old_wp_scripts; + } + + /** + * Update the argument set on a field class. + * + * @param $new_arguments + */ + public function update_arguments( $new_arguments ) { + $this->instance->set_arguments( $new_arguments ); + } + + /** + * Reset the wp_script globals + */ + public function reset_wp_scripts() { + $GLOBALS['wp_scripts'] = new WP_Scripts(); + $GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' ); + } + + /** + * Verify that the field outputs with no errors for each argument set. + * + * @dataProvider argumentsProvider + * + * @param array $arguments Arguments to parse against. + */ + public function test_field_output( $arguments ) { + $this->update_arguments( $arguments ); + + // Check the default HTML output. + // The point of these checks is to ensure that the field doesn't error out with each argument set. + $this->expectOutputRegex( '/data-class=\"' . get_class( $this->instance ) . '\"/' ); + $this->instance->display(); + } + + /** + * Verify that the field saves values correctly to meta. + * + * @dataProvider valuesProvider + * + * @param mixed $value Value to save + * @param mixed $expected_value Optional. Expected value to save. + */ + function test_save_value( $value, $expected_value = false ) { + $this->instance->save( self::$post->ID, $value ); + + // Usually, we only want to pass one value and not a parsed value. Accomodate this here. + if ( false === $expected_value ) { + $expected_value = $value; + } + + // Verify single value is properly saved. + $this->assertEquals( + $expected_value, + get_post_meta( self::$post->ID, get_class( $this->instance ), false ) + ); + } + + /** + * Provide a set of arguments to test against. + * + * Here we provide a default set of arguments to test each field against. + * + * @return array Default argument set. + */ + public function argumentsProvider() { + return [ + [ [] ], + [ [ + 'id' => 'my-ID', + ] ], + [ [ + 'id' => 'my-ID', + 'name' => 'My Name' + ] ], + [ [ + 'id' => 'my-ID', + 'name' => 'My Name', + 'desc' => 'A long description', + ] ], + [ [ + 'id' => 'my-ID', + 'name' => 'My Name', + 'desc' => 'A long description', + 'repeatable' => false, + 'sortable' => true, + ] ], + [ [ + 'id' => 'my-ID', + 'name' => 'My Name', + 'desc' => 'A long description', + 'repeatable' => true, + 'sortable' => false, + ] ], + [ [ + 'id' => 'my-ID', + 'name' => 'My Name', + 'desc' => 'A long description', + 'repeatable' => true, + 'sortable' => true, + 'cols' => 6, + 'readonly' => true, + 'disabled' => true, + 'class' => 'my-fancy-fancy-class', + ] ], + + // @todo:: add default + ]; + } + + /** + * Provide a default set of values to test saving against. + * + * @return array Default values set. + */ + public function valuesProvider() { + return [ + [ [ 'A string' ] ], + [ [ 162735 ] ], + [ [ true ] ], + ]; + } +} diff --git a/tests/fields/test-checkbox-field.php b/tests/fields/test-checkbox-field.php new file mode 100644 index 00000000..8c2b81b3 --- /dev/null +++ b/tests/fields/test-checkbox-field.php @@ -0,0 +1,26 @@ +instance = new CMB_Checkbox( 'CMB_Checkbox', 'Field', [] ); + } +} diff --git a/tests/fields/test-checkbox-multi-field.php b/tests/fields/test-checkbox-multi-field.php new file mode 100644 index 00000000..dd754a97 --- /dev/null +++ b/tests/fields/test-checkbox-multi-field.php @@ -0,0 +1,75 @@ +instance = new CMB_Checkbox_Multi( 'CMB_Checkbox_Multi', 'Field', [] ); + } + + /** + * Test that the number field outputs correctly against an empty value set. + */ + public function testEmptyFieldOutput() { + $field = new CMB_Checkbox_Multi( 'foo', 'Foo', array( 'value' => 'value' ), array( 'options' => array( 'value' => 'value' ) ) ); + + if ( ! self::$post ) { + $this->markTestSkipped( 'Post not found' ); + } + + $this->expectOutputRegex( '/(type=\"checkbox\".*?id=\"foo-cmb-field-0-item-value\".*?checked=\'checked\')/s' ); + + // Trigger output. + $field->html(); + } + + /** + * Test that the number field outputs correctly against a saved value set. + */ + public function testSavedFieldOutput() { + $field = new CMB_Checkbox_Multi( 'foo', 'Foo', array( 'value' => 'value' ), array( 'options' => array( 'value' => 'value', 'value2' => 'value2' ) ) ); + + if ( ! self::$post ) { + $this->markTestSkipped( 'Post not found' ); + } + + $field->save( self::$post->ID, array( 'value2' => 'on' ) ); + + $this->expectOutputRegex( '/(type=\"checkbox\".*?id=\"foo-cmb-field-0-item-value2\".*?checked=\'checked\')/s' ); + + // Trigger output. + $field->html(); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'options' => [ 'Option 1', 'Option 2', 'Option 3' ], + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } +} diff --git a/tests/fields/test-color-field.php b/tests/fields/test-color-field.php new file mode 100644 index 00000000..aa16cff3 --- /dev/null +++ b/tests/fields/test-color-field.php @@ -0,0 +1,26 @@ +instance = new CMB_Color_Picker( 'CMB_Color_Picker', 'Field', [] ); + } +} diff --git a/tests/fields/test-date-field.php b/tests/fields/test-date-field.php new file mode 100644 index 00000000..d3fdf6dc --- /dev/null +++ b/tests/fields/test-date-field.php @@ -0,0 +1,58 @@ +instance = new CMB_Date_Field( 'CMB_Date_Field', 'Field', [] ); + } + + /** + * Test that all required scripts & styles are correctly loaded. + */ + function testAssets() { + global $wp_version; + + $this->reset_wp_scripts(); + + // Register CMB-Scripts as this is a dependency. + wp_register_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); + + $this->instance->enqueue_scripts(); + $this->instance->enqueue_styles(); + + $scripts_output = get_echo( 'wp_print_scripts' ); + $styles_output = get_echo( 'wp_print_styles' ); + + // Scripts + $this->assertContains( '/js/field.datetime.js', $scripts_output ); + $this->assertContains( '/js/cmb.js', $scripts_output ); + if ( version_compare( $wp_version, '4.1', '>=' ) ) { + $this->assertContains( site_url() . '/wp-includes/js/jquery/ui/core.min.js', $scripts_output ); + $this->assertContains( site_url() . '/wp-includes/js/jquery/ui/datepicker.min.js', $scripts_output ); + } else { + $this->assertContains( site_url() . '/wp-includes/js/jquery/ui/jquery.ui.core.min.js', $scripts_output ); + $this->assertContains( site_url() . '/wp-includes/js/jquery/ui/jquery.ui.datepicker.min.js', $scripts_output ); + } + + // Styles + $this->assertContains( 'css/vendor/jquery-ui/jquery-ui.css', $styles_output ); + } +} diff --git a/tests/fields/test-date-time-timestamp-field.php b/tests/fields/test-date-time-timestamp-field.php new file mode 100644 index 00000000..a4d5c4e7 --- /dev/null +++ b/tests/fields/test-date-time-timestamp-field.php @@ -0,0 +1,73 @@ +instance = new CMB_Datetime_Timestamp_Field( 'CMB_Datetime_Timestamp_Field', 'Field', [] ); + } + + /** + * Test that all required scripts & styles are correctly loaded. + */ + function testAssets() { + $this->reset_wp_scripts(); + + // Register CMB-Scripts as this is a dependency. + wp_enqueue_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); + + $this->instance->enqueue_scripts(); + + $scripts_output = get_echo( 'wp_print_scripts' ); + + // Scripts + $this->assertContains( CMB_URL . '/js/cmb.js', $scripts_output ); + $this->assertContains( CMB_URL . '/js/jquery.timePicker.min.js', $scripts_output ); + $this->assertContains( CMB_URL . '/js/field.datetime.js', $scripts_output ); + } + + /** + * Provide a default set of values to test saving against. + * + * @return array Default values set. + */ + public function valuesProvider() { + return [ + [ + [ + [ + 'date' => '12/12/2012', + 'time' => '12:00 am', + ], + ], + [ '1355270400' ], + ], + [ + [ + [ + 'date' => '12/12/2112', + 'time' => '12:00 am', + ], + ], + [ '4510944000' ], + ], + ]; + } +} diff --git a/tests/fields/test-date-timestamp-field.php b/tests/fields/test-date-timestamp-field.php new file mode 100644 index 00000000..8f1c8bc2 --- /dev/null +++ b/tests/fields/test-date-timestamp-field.php @@ -0,0 +1,57 @@ +instance = new CMB_Date_Timestamp_Field( 'CMB_Date_Timestamp_Field', 'Field', [] ); + } + + /** + * Test that all required scripts & styles are correctly loaded. + */ + function testAssets() { + $this->reset_wp_scripts(); + + // Register CMB-Scripts as this is a dependency. + wp_enqueue_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); + + $this->instance->enqueue_scripts(); + + $scripts_output = get_echo( 'wp_print_scripts' ); + + // Scripts + $this->assertContains( CMB_URL . '/js/cmb.js', $scripts_output ); + $this->assertContains( CMB_URL . '/js/jquery.timePicker.min.js', $scripts_output ); + $this->assertContains( CMB_URL . '/js/field.datetime.js', $scripts_output ); + } + + /** + * Provide a default set of values to test saving against. + * + * @return array Default values set. + */ + public function valuesProvider() { + return [ + [ [ '12/12/2012' ], [ '1355270400' ] ], + [ [ '12/12/2112' ], [ '4510944000' ] ], + ]; + } +} diff --git a/tests/fields/test-email-field.php b/tests/fields/test-email-field.php new file mode 100644 index 00000000..a3b1ca3f --- /dev/null +++ b/tests/fields/test-email-field.php @@ -0,0 +1,55 @@ +instance = new CMB_Email_Field( 'CMB_Email_Field', 'Field', [] ); + } + + /** + * Test that the number field outputs correctly against more specific field output. + */ + function testFieldOutput() { + $field = new CMB_Email_Field( 'foo', 'Foo', array( 'hm@hmn.md' ) ); + + if ( ! self::$post ) { + $this->markTestSkipped( 'Post not found' ); + } + + $this->expectOutputRegex( '/(type=\"email\".*?id=\"foo-cmb-field-0\".*?value=\"hm@hmn.md\")/s' ); + + // Trigger output. + $field->html(); + } + + /** + * Provide a default set of values to test saving against. + * + * @return array Default values set. + */ + public function valuesProvider() { + return [ + [ [ 'A string' ] ], + [ [ 'hm@md.com' ] ], + [ [ 'mike@mike.co.uk' ] ], + ]; + } +} diff --git a/tests/fields/test-gmap-field.php b/tests/fields/test-gmap-field.php new file mode 100644 index 00000000..1da4158d --- /dev/null +++ b/tests/fields/test-gmap-field.php @@ -0,0 +1,43 @@ +instance = new CMB_Gmap_Field( 'CMB_Gmap_Field', 'Field', [] ); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'google_api_key' => 'abcdefghijk', + 'default_lat' => '1.234', + 'default_long' => '1.234', + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } +} diff --git a/tests/testGroupField.php b/tests/fields/test-group-field.php similarity index 61% rename from tests/testGroupField.php rename to tests/fields/test-group-field.php index 58cfecb6..93c170a0 100644 --- a/tests/testGroupField.php +++ b/tests/fields/test-group-field.php @@ -1,9 +1,34 @@ instance = new CMB_Group_Field( 'CMB_Group_Field', 'Field', [] ); + } -class GroupFieldTestCase extends WP_UnitTestCase { - + /** + * Test that a fields are correctly added to a group field. + */ function testAddField() { - $group = new CMB_Group_Field( 'group', 'Group Title', array() ); $field1 = new CMB_Text_Field( 'foo', 'Title', array( 1 ) ); $field2 = new CMB_Text_Field( 'bar', 'Title', array( 2, 3 ), array( 'repeatable' => true ) ); @@ -13,11 +38,12 @@ function testAddField() { $this->assertArrayHasKey( 'foo', $group->get_fields() ); $this->assertArrayHasKey( 'bar', $group->get_fields() ); - } + /** + * Test that value retrieval within a group field works correctly. + */ function testGetValues() { - $group = new CMB_Group_Field( 'group', 'Group Title', array() ); $field1 = new CMB_Text_Field( 'foo', 'Title', array() ); $field2 = new CMB_Text_Field( 'bar', 'Title', array() ); @@ -33,11 +59,12 @@ function testGetValues() { ); $this->assertEquals( $group->get_values(), $values ); - } + /** + * Test that vsaving values within a group field works correctly. + */ function testParseSaveValues() { - $group = new CMB_Group_Field( 'group', 'Group Title', array() ); $field1 = new CMB_Text_Field( 'foo', 'Title', array( 1 ) ); $field2 = new CMB_Text_Field( 'bar', 'Title', array( 2, 3 ), array( 'repeatable' => true ) ); @@ -62,11 +89,12 @@ function testParseSaveValues() { $group->parse_save_values(); $this->assertEquals( $group->get_values(), $expected ); - } + /** + * Test that the name attribute works correctly. + */ function testFieldNameAttrValue() { - $group = new CMB_Group_Field( 'group', 'Group Title', array() ); $field1 = new CMB_Text_Field( 'foo', 'Title', array( 1, 2 ) ); @@ -98,11 +126,12 @@ function testFieldNameAttrValue() { $id_attr = $field1->get_the_name_attr(); $this->assertEquals( $id_attr, 'group[cmb-group-12][foo][cmb-field-0]' ); $group->field_index = 0; // Unset - } + /** + * Test that the ID attribute works correctly. + */ function testFieldIdAttrValue() { - $group = new CMB_Group_Field( 'group', 'Group Title', array() ); $field1 = new CMB_Text_Field( 'foo', 'Title', array( 1, 2 ) ); @@ -134,6 +163,83 @@ function testFieldIdAttrValue() { $id_attr = $field1->get_the_id_attr(); $this->assertEquals( $id_attr, 'group-cmb-group-12-foo-cmb-field-0' ); $group->field_index = 0; // Unset + } + + /** + * Verify that the field saves values correctly to meta. + * + * We need to override this method because inner fields must be setup + * in order for saving to work correctly. + * + * @dataProvider valuesProvider + * + * @param mixed $value Value to save + * @param mixed $expected_value Optional. Expected value to save. + */ + function test_save_value( $value, $expected_value = false ) { + $this->instance->add_field( new CMB_Text_Field( 'foo', 'Title', array( 1, 2 ) ) ); + + $this->instance->save( self::$post->ID, $value ); + + // Usually, we only want to pass one value and not a parsed value. Accomodate this here. + if ( false === $expected_value ) { + $expected_value = $value; + } + + // Verify single value is properly saved. + $this->assertEquals( + $expected_value, + get_post_meta( self::$post->ID, get_class( $this->instance ), false ) + ); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'fields' => [ + [ + 'id' => 'gac-4-f-1', + 'name' => 'Text input field', + 'type' => 'text', + ], + [ + 'id' => 'gac-4-f-2', + 'name' => 'Text input field', + 'type' => 'text', + ], + ], + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } + /** + * Provide a default set of values to test saving against. + * + * P.S. the data structure for this field is NUTS. + * + * @return array Values set. + */ + public function valuesProvider() { + return [ + [ + [ [ 'foo' => [ 'A string' ] ] ], + [ [ 'foo' => 'A string' ] ], + ], + [ + [ [ 'foo' => [ 162735 ] ] ], + [ [ 'foo' => 162735 ] ], + ], + [ + [ [ 'foo' => [ true ] ] ], + [ [ 'foo' => true ] ], + ], + ]; } } diff --git a/tests/fields/test-hidden-field.php b/tests/fields/test-hidden-field.php new file mode 100644 index 00000000..217e0b78 --- /dev/null +++ b/tests/fields/test-hidden-field.php @@ -0,0 +1,42 @@ +instance = new CMB_Hidden_Field( 'CMB_Hidden_Field', 'Field', [] ); + } + + /** + * Test that the number field outputs correctly against more specific field output. + */ + function testFieldOutput() { + $field = new CMB_Hidden_Field( 'foo', 'Foo', array( 'value' ) ); + + if ( ! self::$post ) { + $this->markTestSkipped( 'Post not found' ); + } + + $this->expectOutputRegex( '/(type=\"hidden\".*?id=\"foo-cmb-field-0\".*?value=\"value\")/s' ); + + // Trigger output. + $field->html(); + } +} diff --git a/tests/fields/test-image-field.php b/tests/fields/test-image-field.php new file mode 100644 index 00000000..441e3f06 --- /dev/null +++ b/tests/fields/test-image-field.php @@ -0,0 +1,37 @@ +instance = new CMB_Image_Field( 'CMB_Image_Field', 'Field', [] ); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'size' => 175, + ], + [ + 'size' => 175, + 'show_size' => true, + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } +} diff --git a/tests/fields/test-number-field.php b/tests/fields/test-number-field.php new file mode 100644 index 00000000..408bd795 --- /dev/null +++ b/tests/fields/test-number-field.php @@ -0,0 +1,55 @@ +instance = new CMB_Number_Field( 'CMB_Number_Field', 'Field', [] ); + } + + /** + * Test that the number field outputs correctly against more specific field output. + */ + function testFieldOutput() { + $field = new CMB_Number_Field( 'foo', 'Foo', array( 0.5 ), array( 'min' => 0.4, 'max' => 1 ) ); + + if ( ! self::$post ) { + $this->markTestSkipped( 'Post not found' ); + } + + $this->expectOutputRegex( '/min="0.4".*max="1".*(type=\"number\".*?id=\"foo-cmb-field-0\".*?value=\"0.5\")/s' ); + + // Trigger output. + $field->html(); + } + + /** + * Provide a default set of values to test saving against. + * + * @return array Default values set. + */ + public function valuesProvider() { + return [ + [ [ '1' ] ], + [ [ 162735 ] ], + [ [ 0.5 ] ], + ]; + } +} diff --git a/tests/fields/test-post-select-field.php b/tests/fields/test-post-select-field.php new file mode 100644 index 00000000..9ca982e6 --- /dev/null +++ b/tests/fields/test-post-select-field.php @@ -0,0 +1,52 @@ +instance = new CMB_Post_Select( 'CMB_Post_Select', 'Field', [] ); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'use_ajax' => true, + ], + [ + 'use_ajax' => true, + 'multiple' => true, + ], + [ + 'use_ajax' => true, + 'multiple' => true, + 'query' => [ + 'posts_per_page' => 20, + ], + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } +} diff --git a/tests/fields/test-radio-field.php b/tests/fields/test-radio-field.php new file mode 100644 index 00000000..dbacb2da --- /dev/null +++ b/tests/fields/test-radio-field.php @@ -0,0 +1,41 @@ +instance = new CMB_Radio_Field( 'CMB_Radio_Field', 'Field', [] ); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'options' => [ 'Option 1', 'Option 2', 'Option 3' ], + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } +} diff --git a/tests/fields/test-select-field.php b/tests/fields/test-select-field.php new file mode 100644 index 00000000..312b2990 --- /dev/null +++ b/tests/fields/test-select-field.php @@ -0,0 +1,49 @@ +instance = new CMB_Select( 'CMB_Select', 'Field', [] ); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'options' => [ 'Option 1', 'Option 2', 'Option 3' ], + 'select2_options' => [ 'an' => 'option' ], + 'allow_none' => true, + 'multiple' => true, + ], + [ + 'options' => [ 'Option 1', 'Option 2', 'Option 3' ], + 'allow_none' => true, + 'multiple' => false, + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } +} diff --git a/tests/fields/test-taxonomy-field.php b/tests/fields/test-taxonomy-field.php new file mode 100644 index 00000000..2a8299f7 --- /dev/null +++ b/tests/fields/test-taxonomy-field.php @@ -0,0 +1,53 @@ +instance = new CMB_Taxonomy( 'CMB_Taxonomy', 'Field', [] ); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'taxonomy' => 'post_tag', + 'hide_empty' => false, + 'multiple' => true, + ], + [ + 'taxonomy' => 'post_tag', + 'hide_empty' => true, + 'multiple' => true, + ], + [ + 'taxonomy' => 'post_tag', + 'hide_empty' => false, + 'multiple' => false, + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } +} diff --git a/tests/fields/test-text-field.php b/tests/fields/test-text-field.php new file mode 100644 index 00000000..87cb20fa --- /dev/null +++ b/tests/fields/test-text-field.php @@ -0,0 +1,26 @@ +instance = new CMB_Text_Field( 'CMB_Text_Field', 'Field', [] ); + } +} diff --git a/tests/fields/test-text-small-field.php b/tests/fields/test-text-small-field.php new file mode 100644 index 00000000..1bbfc280 --- /dev/null +++ b/tests/fields/test-text-small-field.php @@ -0,0 +1,26 @@ +instance = new CMB_Text_Small_Field( 'CMB_Text_Small_Field', 'Field', [] ); + } +} diff --git a/tests/fields/test-textarea-code-field.php b/tests/fields/test-textarea-code-field.php new file mode 100644 index 00000000..b6205e38 --- /dev/null +++ b/tests/fields/test-textarea-code-field.php @@ -0,0 +1,26 @@ +instance = new CMB_Textarea_Field_Code( 'CMB_Textarea_Field_Code', 'Field', [] ); + } +} diff --git a/tests/fields/test-textarea-field.php b/tests/fields/test-textarea-field.php new file mode 100644 index 00000000..4a1469a3 --- /dev/null +++ b/tests/fields/test-textarea-field.php @@ -0,0 +1,26 @@ +instance = new CMB_Textarea_Field( 'CMB_Textarea_Field', 'Field', [] ); + } +} diff --git a/tests/fields/test-time-field.php b/tests/fields/test-time-field.php new file mode 100644 index 00000000..f7dca55b --- /dev/null +++ b/tests/fields/test-time-field.php @@ -0,0 +1,45 @@ +instance = new CMB_Time_Field( 'CMB_Time_Field', 'Field', [] ); + } + + /** + * Test that all required scripts & styles are correctly loaded. + */ + function testAssets() { + $this->reset_wp_scripts(); + + // Register CMB-Scripts as this is a dependency. + wp_enqueue_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); + + $this->instance->enqueue_scripts(); + + $scripts_output = get_echo( 'wp_print_scripts' ); + + // Scripts + $this->assertContains( CMB_URL . '/js/cmb.js', $scripts_output ); + $this->assertContains( CMB_URL . '/js/jquery.timePicker.min.js', $scripts_output ); + $this->assertContains( CMB_URL . '/js/field.datetime.js', $scripts_output ); + } +} diff --git a/tests/fields/test-title-code-field.php b/tests/fields/test-title-code-field.php new file mode 100644 index 00000000..29aa1061 --- /dev/null +++ b/tests/fields/test-title-code-field.php @@ -0,0 +1,26 @@ +instance = new CMB_Title( 'CMB_Title', 'Field', [] ); + } +} diff --git a/tests/fields/test-url-code-field.php b/tests/fields/test-url-code-field.php new file mode 100644 index 00000000..8f886ca3 --- /dev/null +++ b/tests/fields/test-url-code-field.php @@ -0,0 +1,26 @@ +instance = new CMB_URL_Field( 'CMB_URL_Field', 'Field', [] ); + } +} diff --git a/tests/fields/test-wysiwyg-code-field.php b/tests/fields/test-wysiwyg-code-field.php new file mode 100644 index 00000000..5fa1ffc8 --- /dev/null +++ b/tests/fields/test-wysiwyg-code-field.php @@ -0,0 +1,41 @@ +instance = new CMB_wysiwyg( 'CMB_wysiwyg', 'Field', [] ); + } + + /** + * Update our default argument set with specific args. + * + * @return array + */ + public function argumentsProvider() { + $args = [ + [ + 'options' => [ 'editor_height' => 500 ], + ], + ]; + + return array_merge( $args, parent::argumentsProvider() ); + } +} diff --git a/tests/testCmbMetaBoxClass.php b/tests/testCmbMetaBoxClass.php new file mode 100644 index 00000000..e7b60e73 --- /dev/null +++ b/tests/testCmbMetaBoxClass.php @@ -0,0 +1,188 @@ +factory(); + } else { + $factory = $this->factory; + } + + // Create a test post. + $this->post_id = $factory->post->create( + array( + 'post_author' => get_current_user_id(), + 'post_title' => 'My test post', + 'post_type' => 'post', + 'post_status' => 'publish', + ) + ); + + $this->simple_fields = array( + 'title' => 'Simple', + 'pages' => 'post', + 'fields' => array( + array( + 'id' => 'text', + 'name' => 'Simple', + 'type' => 'text', + 'repeatable' => false, + ), + ), + ); + + $this->group_fields = array( + 'title' => 'Simple', + 'pages' => 'post', + 'fields' => array( + array( + 'id' => 'text', + 'name' => 'Simple', + 'type' => 'group', + 'fields' => array( + array( + 'id' => 'text', + 'name' => 'Simple', + 'type' => 'text', + ), + array( + 'id' => 'text2', + 'name' => 'Simple 2', + 'type' => 'text', + ), + ), + ), + ), + ); + + // Assign an admin user as we now check that you have proper permissions before passing a field. + wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) ); + + } + + /** + * Clean up after a test. + */ + function tearDown() { + wp_delete_post( $this->post_id ); + parent::tearDown(); + } + + + function test_values_from_init_non_repeatable() { + + $values = array( 1, 2, 3, 4, 5 ); + + $field = $this->intialize_fields( $this->simple_fields, $values ); + + // Check that a non-repeatable field only returns one value. + // The new instance of the field is 1. + $this->assertEquals( array( 1 ), $field->values ); + } + + function test_values_from_init_repeatable() { + + // Change simple field to be a repeatable. + $this->simple_fields['fields'][0]['repeatable'] = true; + + $values = array( 1, 2, 3, 4, 5 ); + + $field = $this->intialize_fields( $this->simple_fields, $values ); + + // Check that a non-repeatable field only returns one value. + // The new instance of the field is 1. + $this->assertEquals( array( 1, 2, 3, 4, 5 ), $field->values ); + } + + function test_values_from_init_group_non_repeatable() { + + $values = array( + array( + 'text' => array( 'one', 'two', 'three' ), + 'text2' => array( 'four', 'five', 'six' ), + ) + ); + + $field = $this->intialize_fields( $this->group_fields, $values ); + + // Check that a non-repeatable field only returns one value. + // The new instance of the field is 1. + $this->assertEquals( array( array( 'text' => 'one', 'text2' => 'four' ) ), $field->values ); + } + + function test_values_from_init_group_repeatable() { + + // Change fields to be a repeatable. + $this->group_fields['fields'][0]['fields'][0]['repeatable'] = true; + $this->group_fields['fields'][0]['fields'][1]['repeatable'] = true; + + $values = array( + array( + 'text' => array( 'one', 'two', 'three' ), + 'text2' => array( 'four', 'five', 'six' ), + ), + ); + + $field = $this->intialize_fields( $this->group_fields, $values ); + + // Check that a non-repeatable field only returns one value. + // The new instance of the field is 1. + $this->assertEquals( + array( + array( + 'text' => array( 'one', 'two', 'three' ), + 'text2' => array( 'four', 'five', 'six' ), + ), + ), + $field->values + ); + } + + /** + * Mock up a CMB_MetaBox instance, save a field value and the return the boxes for testing + * + * @param $fields + * @param $values + * @return object Field type class instance. + */ + private function intialize_fields( $fields, $values ) { + $boxes = new CMB_Meta_Box( $fields ); + $boxes->init_fields( $this->post_id ); + + // Save some values for testing. + $boxes->fields[0]->save( $this->post_id, $values ); + + // Re-initialize the boxes and check the data. + $boxes->init_fields( $this->post_id ); + + return $boxes->fields[1]; + } +} diff --git a/tests/testDateField.php b/tests/testDateField.php deleted file mode 100644 index 8c2f070f..00000000 --- a/tests/testDateField.php +++ /dev/null @@ -1,103 +0,0 @@ -old_wp_scripts = isset( $GLOBALS['wp_scripts'] ) ? $GLOBALS['wp_scripts'] : null; - $GLOBALS['wp_scripts'] = new WP_Scripts(); - $GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' ); - } - - function tearDown() { - $GLOBALS['wp_scripts'] = $this->old_wp_scripts; - parent::tearDown(); - } - - /** - * Test that all required scripts & styles are correctly loaded. - */ - function testDateFieldAssets() { - global $wp_version; - - $field = new CMB_Date_Field( 'foo', 'Title', array() ); - - // Register CMB-Scripts as this is a dependency. - wp_register_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); - - $field->enqueue_scripts(); - $field->enqueue_styles(); - - $scripts_output = get_echo( 'wp_print_scripts' ); - $styles_output = get_echo( 'wp_print_styles' ); - - // Scripts - $this->assertContains( '/js/field.datetime.js', $scripts_output ); - $this->assertContains( '/js/cmb.js', $scripts_output ); - if ( version_compare( $wp_version, '4.1', '>=' ) ) { - $this->assertContains( site_url() . '/wp-includes/js/jquery/ui/core.min.js', $scripts_output ); - $this->assertContains( site_url() . '/wp-includes/js/jquery/ui/datepicker.min.js', $scripts_output ); - } else { - $this->assertContains( site_url() . '/wp-includes/js/jquery/ui/jquery.ui.core.min.js', $scripts_output ); - $this->assertContains( site_url() . '/wp-includes/js/jquery/ui/jquery.ui.datepicker.min.js', $scripts_output ); - } - - // Styles - $this->assertContains( 'css/vendor/jquery-ui/jquery-ui.css', $styles_output ); - - } - - function testTimeFieldAssets() { - - $field = new CMB_Time_Field( 'foo', 'Title', array() ); - - // Register CMB-Scripts as this is a dependency. - wp_enqueue_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); - - $field->enqueue_scripts(); - - $scripts_output = get_echo( 'wp_print_scripts' ); - - // Scripts - $this->assertContains( CMB_URL . '/js/cmb.js', $scripts_output ); - $this->assertContains( CMB_URL . '/js/jquery.timePicker.min.js', $scripts_output ); - $this->assertContains( CMB_URL . '/js/field.datetime.js', $scripts_output ); - - } - - function testDateTimestampFieldAssets() { - - $field = new CMB_Date_Timestamp_Field( 'foo', 'Title', array() ); - - // Register CMB-Scripts as this is a dependency. - wp_enqueue_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); - - $field->enqueue_scripts(); - - $scripts_output = get_echo( 'wp_print_scripts' ); - - // Scripts - $this->assertContains( CMB_URL . '/js/cmb.js', $scripts_output ); - $this->assertContains( CMB_URL . '/js/jquery.timePicker.min.js', $scripts_output ); - $this->assertContains( CMB_URL . '/js/field.datetime.js', $scripts_output ); - - } - - function testDatetimeTimestampFieldAssets() { - - $field = new CMB_Datetime_Timestamp_Field( 'foo', 'Title', array() ); - - // Register CMB-Scripts as this is a dependency. - wp_enqueue_script( 'cmb-scripts', trailingslashit( CMB_URL ) . 'js/cmb.js', array( 'jquery' ) ); - - $field->enqueue_scripts(); - - $scripts_output = get_echo( 'wp_print_scripts' ); - - // Scripts - $this->assertContains( CMB_URL . '/js/cmb.js', $scripts_output ); - $this->assertContains( CMB_URL . '/js/jquery.timePicker.min.js', $scripts_output ); - $this->assertContains( CMB_URL . '/js/field.datetime.js', $scripts_output ); - - } -} diff --git a/tests/testField.php b/tests/testField.php index fc5be6a0..d4f82249 100644 --- a/tests/testField.php +++ b/tests/testField.php @@ -4,27 +4,29 @@ class FieldTestCase extends WP_UnitTestCase { private $post; - function setUp() { + private $users = array(); + function setUp() { parent::setUp(); - $args = array( - 'post_author' => 1, - 'post_status' => 'publish', - 'post_content' => rand_str(), - 'post_title' => rand_str(), - 'post_type' => 'post', - ); - - $id = wp_insert_post( $args ); - - $this->post = get_post( $id ); + // Setup some users to test our display logic. + $this->users['admin'] = $this->factory->user->create( array( 'role' => 'administrator' ) ); + $this->users['author'] = $this->factory->user->create( array( 'role' => 'author' ) ); + // Setup a post for testing is_displayed. + $this->post = $this->factory->post->create_and_get( array( + 'post_author' => 1, + 'post_status' => 'publish', + 'post_content' => rand_str(), + 'post_title' => rand_str(), + 'post_type' => 'post', + ) ); } function tearDown() { wp_delete_post( $this->post->ID, true ); unset( $this->post ); + wp_set_current_user( 0 ); parent::tearDown(); } @@ -144,7 +146,6 @@ function testEmptyFieldOutput() { // Trigger output. $field->html(); - } function testSavedFieldOutput() { @@ -163,4 +164,84 @@ function testSavedFieldOutput() { $field->html(); } + function testDisplayedRepeatableButtonDiv() { + $field = new CMB_Text_Field( 'foo', 'Title', array( 1 ), array( 'repeatable' => true ) ); + + // Set expectation for an empty output. + $this->expectOutputRegex( '/\
/' ); + + hmcmb_invoke_method( $field, 'repeatable_button_markup' ); + } + + function testDisplayedRepeatableButtonButton() { + $field = new CMB_Text_Field( 'foo', 'Title', array( 1 ), array( 'repeatable' => true ) ); + + // Set expectation for an empty output. + $this->expectOutputRegex( '/\