field = $field; } /** * Default fallback. Allows rendering fields via "cmb_render_$name" hook * @since 1.0.0 * @param string $name Non-existent method name * @param array $arguments All arguments passed to the method */ public function __call( $name, $arguments ) { // When a non-registered field is called, send it through an action. do_action( "cmb_render_$name", $this->field->args(), $this->field->escaped_value(), $this->field->object_id, $this->field->object_type, $this ); } /** * Render a field (and handle repeatable) * @since 1.1.0 */ public function render() { if ( $this->field->args( 'repeatable' ) ) { $this->render_repeatable_field(); } else { $this->_render(); } } /** * Render a field type * @since 1.1.0 */ protected function _render() { echo $this->{$this->field->type()}(); } /** * Checks if we can get a post object, and if so, uses `get_the_terms` which utilizes caching * @since 1.0.2 * @return mixed Array of terms on success */ public function get_object_terms() { $object_id = $this->field->object_id; $taxonomy = $this->field->args( 'taxonomy' ); if ( ! $post = get_post( $object_id ) ) { $cache_key = 'cmb-cache-'. $taxonomy .'-'. $object_id; // Check cache $cached = $test = get_transient( $cache_key ); if ( $cached ) return $cached; $cached = wp_get_object_terms( $object_id, $taxonomy ); // Do our own (minimal) caching. Long enough for a page-load. $set = set_transient( $cache_key, $cached, 60 ); return $cached; } // WP caches internally so it's better to use return get_the_terms( $post, $taxonomy ); } /** * Determine a file's extension * @since 1.0.0 * @param string $file File url * @return string|false File extension or false */ public function get_file_ext( $file ) { $parsed = @parse_url( $file, PHP_URL_PATH ); return $parsed ? strtolower( pathinfo( $parsed, PATHINFO_EXTENSION ) ) : false; } /** * Determines if a file has a valid image extension * @since 1.0.0 * @param string $file File url * @return bool Whether file has a valid image extension */ public function is_valid_img_ext( $file ) { $file_ext = $this->get_file_ext( $file ); $this->valid = empty( $this->valid ) ? (array) apply_filters( 'cmb_valid_img_types', array( 'jpg', 'jpeg', 'png', 'gif', 'ico', 'icon' ) ) : $this->valid; return ( $file_ext && in_array( $file_ext, $this->valid ) ); } /** * Handles parsing and filtering attributes while preserving any passed in via field config. * @since 1.1.0 * @param array $args Override arguments * @param string $element Element for filter * @param array $defaults Default arguments * @return array Parsed and filtered arguments */ public function parse_args( $args, $element, $defaults ) { return wp_parse_args( apply_filters( "cmb_{$element}_attributes", $this->field->maybe_set_attributes( $args ), $this->field, $this ), $defaults ); } /** * Combines attributes into a string for a form element * @since 1.1.0 * @param array $attrs Attributes to concatenate * @param array $attr_exclude Attributes that should NOT be concatenated * @return string String of attributes for form element */ public function concat_attrs( $attrs, $attr_exclude = array() ) { $attributes = ''; foreach ( $attrs as $attr => $val ) { if ( ! in_array( $attr, (array) $attr_exclude, true ) ) $attributes .= sprintf( ' %s="%s"', $attr, $val ); } return $attributes; } /** * Generates html for an option element * @since 1.1.0 * @param string $opt_label Option label * @param string $opt_value Option value * @param mixed $selected Selected attribute if option is selected * @return string Generated option element html */ public function option( $opt_label, $opt_value, $selected ) { return sprintf( "\t".'', $opt_value, selected( $selected, true, false ), $opt_label )."\n"; } /** * Generates options html * @since 1.1.0 * @param array $args Optional arguments * @param string $method Method to generate individual option item * @return string Concatenated html options */ public function concat_options( $args = array(), $method = 'list_input' ) { $options = (array) $this->field->args( 'options' ); $saved_value = $this->field->escaped_value(); $value = $saved_value ? $saved_value : $this->field->args( 'default' ); $_options = ''; $i = 1; foreach ( $options as $option_key => $option ) { // Check for the "old" way $opt_label = is_array( $option ) && array_key_exists( 'name', $option ) ? $option['name'] : $option; $opt_value = is_array( $option ) && array_key_exists( 'value', $option ) ? $option['value'] : $option_key; // Check if this option is the value of the input $is_current = $value == $opt_value; if ( ! empty( $args ) ) { // Clone args & modify for just this item $this_args = $args; $this_args['value'] = $opt_value; $this_args['label'] = $opt_label; if ( $is_current ) $this_args['checked'] = 'checked'; $_options .= $this->$method( $this_args, $i ); } else { $_options .= $this->option( $opt_label, $opt_value, $is_current ); } $i++; } return $_options; } /** * Generates html for list item with input * @since 1.1.0 * @param array $args Override arguments * @param int $i Iterator value * @return string Gnerated list item html */ public function list_input( $args = array(), $i ) { $args = $this->parse_args( $args, 'list_input', array( 'type' => 'radio', 'class' => 'cmb_option', 'name' => $this->_name(), 'id' => $this->_id( $i ), 'value' => $this->field->escaped_value(), 'label' => '', ) ); return sprintf( "\t".'
  • '."\n", $this->concat_attrs( $args, 'label' ), $args['id'], $args['label'] ); } /** * Generates html for list item with checkbox input * @since 1.1.0 * @param array $args Override arguments * @param int $i Iterator value * @return string Gnerated list item html */ public function list_input_checkbox( $args, $i ) { unset( $args['selected'] ); $saved_value = $this->field->escaped_value(); if ( is_array( $saved_value ) && in_array( $args['value'], $saved_value ) ) { $args['checked'] = 'checked'; } return $this->list_input( $args, $i ); } /** * Generates repeatable field table markup * @since 1.0.0 */ public function render_repeatable_field() { $table_id = $this->field->id() .'_repeat'; $this->_desc( true, true ); ?> repeatable_rows(); ?>

    iterator = 0; } /** * Generates repeatable field rows * @since 1.1.0 */ public function repeatable_rows() { $meta_value = $this->field->escaped_value(); // check for default content $default = $this->field->args( 'default' ); // check for saved data if ( ! empty( $meta_value ) ) { $meta_value = is_array( $meta_value ) ? array_filter( $meta_value ) : $meta_value; $meta_value = ! empty( $meta_value ) ? $meta_value : $default; } else { $meta_value = $default; } // Loop value array and add a row if ( ! empty( $meta_value ) ) { foreach ( (array) $meta_value as $val ) { $this->field->escaped_value = $val; $this->repeat_row(); $this->iterator++; } } else { // Otherwise add one row $this->repeat_row(); } // Then add an empty row $this->field->escaped_value = ''; $this->iterator = $this->iterator ? $this->iterator : 1; $this->repeat_row( 'empty-row' ); } /** * Generates a repeatable row's markup * @since 1.1.0 * @param string $class Repeatable table row's class */ protected function repeat_row( $class = 'repeat-row' ) { ?> _render(); ?> field->args( 'repeatable' ) || $this->iterator > 0 ) { return ''; } $tag = $paragraph ? 'p' : 'span'; $desc = "\n<$tag class=\"cmb_metabox_description\">{$this->field->args( 'description' )}\n"; if ( $echo ) echo $desc; return $desc; } /** * Generate field name attribute * @since 1.1.0 * @param string $suffix For multi-part fields * @return string Name attribute */ public function _name( $suffix = '' ) { return $this->field->args( '_name' ) . ( $this->field->args( 'repeatable' ) ? '['. $this->iterator .']' : '' ) . $suffix; } /** * Generate field id attribute * @since 1.1.0 * @param string $suffix For multi-part fields * @return string Id attribute */ public function _id( $suffix = '' ) { return $this->field->id() . $suffix . ( $this->field->args( 'repeatable' ) ? '_'. $this->iterator .'" data-iterator="'. $this->iterator : '' ); } /** * Handles outputting an 'input' element * @since 1.1.0 * @param array $args Override arguments * @return string Form input element */ public function input( $args = array() ) { $args = $this->parse_args( $args, 'input', array( 'type' => 'text', 'class' => 'regular-text', 'name' => $this->_name(), 'id' => $this->_id(), 'value' => $this->field->escaped_value(), 'desc' => $this->_desc( true ), ) ); return sprintf( '%s', $this->concat_attrs( $args, 'desc' ), $args['desc'] ); } /** * Handles outputting an 'textarea' element * @since 1.1.0 * @param array $args Override arguments * @return string Form textarea element */ public function textarea( $args = array() ) { $args = $this->parse_args( $args, 'textarea', array( 'class' => 'cmb_textarea', 'name' => $this->_name(), 'id' => $this->_id(), 'cols' => 60, 'rows' => 10, 'value' => $this->field->escaped_value( 'esc_textarea' ), 'desc' => $this->_desc( true ), ) ); return sprintf( '%s%s', $this->concat_attrs( $args, array( 'desc', 'value' ) ), $args['value'], $args['desc'] ); } /** * Begin Field Types */ public function text() { return $this->input(); } public function text_small() { return $this->input( array( 'class' => 'cmb_text_small', 'desc' => $this->_desc() ) ); } public function text_medium() { return $this->input( array( 'class' => 'cmb_text_medium', 'desc' => $this->_desc() ) ); } public function text_email() { return $this->input( array( 'class' => 'cmb_text_email cmb_text_medium', 'type' => 'email' ) ); } public function text_url() { return $this->input( array( 'class' => 'cmb_text_url cmb_text_medium regular-text', 'value' => $this->field->escaped_value( 'esc_url' ) ) ); } public function text_date() { return $this->input( array( 'class' => 'cmb_text_small cmb_datepicker', 'desc' => $this->_desc() ) ); } public function text_time() { return $this->input( array( 'class' => 'cmb_timepicker text_time', 'desc' => $this->_desc() ) ); } public function text_money() { return ( ! $this->field->args( 'before' ) ? '$ ' : ' ' ) . $this->input( array( 'class' => 'cmb_text_money', 'desc' => $this->_desc() ) ); } public function textarea_small() { return $this->textarea( array( 'class' => 'cmb_textarea_small', 'rows' => 4 ) ); } public function textarea_code() { return sprintf( '
    %s
    ', $this->textarea( array( 'class' => 'cmb_textarea_code' ) ) ); } public function wysiwyg( $args = array() ) { extract( $this->parse_args( $args, 'input', array( 'id' => $this->_id(), 'value' => $this->field->escaped_value( 'stripslashes' ), 'desc' => $this->_desc( true ), 'options' => $this->field->args( 'options' ), ) ) ); wp_editor( $value, $id, $options ); echo $desc; } public function text_date_timestamp() { $meta_value = $this->field->escaped_value(); $value = ! empty( $meta_value ) ? date( $this->field->args( 'date_format' ), $meta_value ) : ''; return $this->input( array( 'class' => 'cmb_text_small cmb_datepicker', 'value' => $value ) ); } public function text_datetime_timestamp( $meta_value = '' ) { $desc = ''; if ( ! $meta_value ) { $meta_value = $this->field->escaped_value(); // This will be used if there is a select_timezone set for this field $tz_offset = $this->field->field_timezone_offset(); if ( ! empty( $tz_offset ) ) { $meta_value -= $tz_offset; } $desc = $this->_desc(); } $inputs = array( $this->input( array( 'class' => 'cmb_text_small cmb_datepicker', 'name' => $this->_name( '[date]' ), 'id' => $this->_id( '_date' ), 'value' => ! empty( $meta_value ) ? date( $this->field->args( 'date_format' ), $meta_value ) : '', 'desc' => '', ) ), $this->input( array( 'class' => 'cmb_timepicker text_time', 'name' => $this->_name( '[time]' ), 'id' => $this->_id( '_time' ), 'value' => ! empty( $meta_value ) ? date( $this->field->args( 'time_format' ), $meta_value ) : '', 'desc' => $desc, ) ) ); return implode( "\n", $inputs ); } public function text_datetime_timestamp_timezone() { $meta_value = $this->field->escaped_value(); $datetime = unserialize( $meta_value ); $meta_value = $tzstring = false; if ( $datetime && $datetime instanceof DateTime ) { $tz = $datetime->getTimezone(); $tzstring = $tz->getName(); $meta_value = $datetime->getTimestamp() + $tz->getOffset( new DateTime( 'NOW' ) ); } $inputs = $this->text_datetime_timestamp( $meta_value ); $inputs .= ''. $this->_desc(); return $inputs; } public function select_timezone() { $this->field->args['default'] = $this->field->args( 'default' ) ? $this->field->args( 'default' ) : cmb_Meta_Box::timezone_string(); $meta_value = $this->field->escaped_value(); return ''; } public function colorpicker() { $meta_value = $this->field->escaped_value(); $hex_color = '(([a-fA-F0-9]){3}){1,2}$'; if ( preg_match( '/^' . $hex_color . '/i', $meta_value ) ) // Value is just 123abc, so prepend #. $meta_value = '#' . $meta_value; elseif ( ! preg_match( '/^#' . $hex_color . '/i', $meta_value ) ) // Value doesn't match #123abc, so sanitize to just #. $meta_value = "#"; return $this->input( array( 'class' => 'cmb_colorpicker cmb_text_small', 'value' => $meta_value ) ); } public function title() { extract( $this->parse_args( array(), 'title', array( 'tag' => $this->field->object_type == 'post' ? 'h5' : 'h3', 'class' => 'cmb_metabox_title', 'name' => $this->field->args( 'name' ), 'desc' => $this->_desc( true ), ) ) ); return sprintf( '<%1$s class="%2$s">%3$s%4$s', $tag, $class, $name, $desc ); } public function select( $args = array() ) { $args = $this->parse_args( $args, 'select', array( 'class' => 'cmb_select', 'name' => $this->_name(), 'id' => $this->_id(), 'desc' => $this->_desc( true ), 'options' => $this->concat_options(), ) ); $attrs = $this->concat_attrs( $args, array( 'desc', 'options' ) ); return sprintf( '%s%s', $attrs, $args['options'], $args['desc'] ); } public function taxonomy_select() { $names = $this->get_object_terms(); $saved_term = is_wp_error( $names ) || empty( $names ) ? $this->field->args( 'default' ) : $names[0]->slug; $terms = get_terms( $this->field->args( 'taxonomy' ), 'hide_empty=0' ); $options = ''; foreach ( $terms as $term ) { $selected = $saved_term == $term->slug; $options .= $this->option( $term->name, $term->slug, $selected ); } return $this->select( array( 'options' => $options ) ); } public function radio( $args = array(), $type = 'radio' ) { extract( $this->parse_args( $args, $type, array( 'class' => 'cmb_radio_list cmb_list', 'options' => $this->concat_options( array( 'label' => 'test' ) ), 'desc' => $this->_desc( true ), ) ) ); return sprintf( '
      %s
    %s', $class, $options, $desc ); } public function radio_inline() { return $this->radio( array(), 'radio_inline' ); } public function multicheck( $type = 'checkbox' ) { return $this->radio( array( 'class' => 'cmb_checkbox_list cmb_list', 'options' => $this->concat_options( array( 'type' => 'checkbox', 'name' => $this->_name() .'[]' ), 'list_input_checkbox' ) ), $type ); } public function multicheck_inline() { $this->multicheck( 'multicheck_inline' ); } public function checkbox() { $meta_value = $this->field->escaped_value(); $args = array( 'type' => 'checkbox', 'class' => 'cmb_option cmb_list', 'value' => 'on', 'desc' => '' ); if ( ! empty( $meta_value ) ) { $args['checked'] = 'checked'; } return sprintf( '%s ', $this->input( $args ), $this->_id(), $this->_desc() ); } public function taxonomy_radio() { $names = $this->get_object_terms(); $saved_term = is_wp_error( $names ) || empty( $names ) ? $this->field->args( 'default' ) : $names[0]->slug; $terms = get_terms( $this->field->args( 'taxonomy' ), 'hide_empty=0' ); $options = ''; $i = 1; if ( ! $terms ) { $options .= '
  • '; } else { foreach ( $terms as $term ) { $args = array( 'value' => $term->slug, 'label' => $term->name, ); if ( $saved_term == $term->slug ) { $args['checked'] = 'checked'; } $options .= $this->list_input( $args, $i ); $i++; } } return $this->radio( array( 'options' => $options ), 'taxonomy_radio' ); } public function taxonomy_radio_inline() { $this->taxonomy_radio(); } public function taxonomy_multicheck() { $names = $this->get_object_terms(); $saved_terms = is_wp_error( $names ) || empty( $names ) ? $this->field->args( 'default' ) : wp_list_pluck( $names, 'slug' ); $terms = get_terms( $this->field->args( 'taxonomy' ), 'hide_empty=0' ); $name = $this->_name() .'[]'; $options = ''; $i = 1; if ( ! $terms ) { $options .= '
  • '; } else { foreach ( $terms as $term ) { $args = array( 'value' => $term->slug, 'label' => $term->name, 'type' => 'checkbox', 'name' => $name, ); if ( is_array( $saved_terms ) && in_array( $term->slug, $saved_terms ) ) { $args['checked'] = 'checked'; } $options .= $this->list_input( $args, $i ); $i++; } } return $this->radio( array( 'class' => 'cmb_checkbox_list cmb_list', 'options' => $options ), 'taxonomy_multicheck' ); } public function taxonomy_multicheck_inline() { $this->taxonomy_multicheck(); } public function file_list() { $meta_value = $this->field->escaped_value(); $name = $this->_name(); echo $this->input( array( 'type' => 'hidden', 'class' => 'cmb_upload_file cmb_upload_list', 'size' => 45, 'desc' => '', 'value' => '', ) ), $this->input( array( 'type' => 'button', 'class' => 'cmb_upload_button button cmb_upload_list', 'value' => __( 'Add or Upload File', 'cmb' ), 'name' => '', 'id' => '', ) ); echo '
      '; if ( $meta_value && is_array( $meta_value ) ) { foreach ( $meta_value as $id => $fullurl ) { $id_input = $this->input( array( 'type' => 'hidden', 'value' => $fullurl, 'name' => $name .'['. $id .']', 'id' => 'filelist-'. $id, 'desc' => '', 'class' => '', ) ); if ( $this->is_valid_img_ext( $fullurl ) ) { echo '
    • ', wp_get_attachment_image( $id, $this->field->args( 'preview_size' ) ), '

      '. __( 'Remove Image', 'cmb' ) .'

      '. $id_input .'
    • '; } else { $parts = explode( '/', $fullurl ); for ( $i = 0; $i < count( $parts ); ++$i ) { $title = $parts[$i]; } echo '
    • ', __( 'File:', 'cmb' ), ' ', $title, '    ('. __( 'Download', 'cmb' ) .' / '. __( 'Remove', 'cmb' ) .') '. $id_input .'
    • '; } } } echo '
    '; } public function file() { $meta_value = $this->field->escaped_value(); $allow = $this->field->args( 'allow' ); $input_type = ( 'url' == $allow || ( is_array( $allow ) && in_array( 'url', $allow ) ) ) ? 'text' : 'hidden'; echo $this->input( array( 'type' => $input_type, 'class' => 'cmb_upload_file', 'size' => 45, 'desc' => '', ) ), '', $this->_desc( true ); $cached_id = $this->_id(); // Reset field args for attachment ID $args = $this->field->args(); $args['id'] = $args['_id'] . '_id'; unset( $args['_id'], $args['_name'] ); // And get new field object $this->field = new cmb_Meta_Box_field( $args, $this->field->group ); // Get ID value $_id_value = $this->field->escaped_value( 'absint' ); // If there is no ID saved yet, try to get it from the url if ( $meta_value && ! $_id_value ) { $_id_value = cmb_Meta_Box::image_id_from_url( esc_url_raw( $meta_value ) ); } echo $this->input( array( 'type' => 'hidden', 'class' => 'cmb_upload_file_id', 'value' => $_id_value, 'desc' => '', ) ), '
    '; if ( ! empty( $meta_value ) ) { if ( $this->is_valid_img_ext( $meta_value ) ) { echo '
    '; echo ''; echo '

    '. __( 'Remove Image', 'cmb' ) .'

    '; echo '
    '; } else { // $file_ext = $this->get_file_ext( $meta_value ); $parts = explode( '/', $meta_value ); for ( $i = 0; $i < count( $parts ); ++$i ) { $title = $parts[$i]; } echo __( 'File:', 'cmb' ), ' ', $title, '    ('. __( 'Download', 'cmb' ) .' / '. __( 'Remove', 'cmb' ) .')'; } } echo '
    '; } public function oembed() { echo $this->input( array( 'class' => 'cmb_oembed regular-text', 'data-objectid' => $this->field->object_id, 'data-objecttype' => $this->field->object_type ) ), '', '
    '; if ( $meta_value = $this->field->escaped_value() ) { echo cmb_Meta_Box_ajax::get_oembed( $meta_value, $this->field->object_id, array( 'object_type' => $this->field->object_type, 'oembed_args' => array( 'width' => '640' ), 'field_id' => $this->_id(), ) ); } echo '
    '; } }