PluginProbe
Cool Flipbox – Shortcode & Gutenberg Block / 1.4
Cool Flipbox – Shortcode & Gutenberg Block v1.4
trunk 1.4 1.5 1.6 1.6.2 1.7.1 1.8.3 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1
flip-boxes / CMB2 / includes / CMB2_Field.php

CMB2_Field.php in Cool Flipbox – Shortcode & Gutenberg Block 1.4, at CMB2/includes/CMB2_Field.php

1,551 lines 44.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CMB2 field objects
4 *
5 * @since 1.1.0
6 *
7 * @category WordPress_Plugin
8 * @package CMB2
9 * @author CMB2 team
10 * @license GPL-2.0+
11 * @link https://cmb2.io
12 *
13 * @method string _id()
14 * @method string type()
15 * @method mixed fields()
16 */
17 class CMB2_Field extends CMB2_Base {
18
19 /**
20 * The object properties name.
21 *
22 * @var string
23 * @since 2.2.3
24 */
25 protected $properties_name = 'args';
26
27 /**
28 * Field arguments
29 *
30 * @var mixed
31 * @since 1.1.0
32 */
33 public $args = array();
34
35 /**
36 * Field group object or false (if no group)
37 *
38 * @var mixed
39 * @since 1.1.0
40 */
41 public $group = false;
42
43 /**
44 * Field meta value
45 *
46 * @var mixed
47 * @since 1.1.0
48 */
49 public $value = null;
50
51 /**
52 * Field meta value
53 *
54 * @var mixed
55 * @since 1.1.0
56 */
57 public $escaped_value = null;
58
59 /**
60 * Grouped Field's current numeric index during the save process
61 *
62 * @var mixed
63 * @since 2.0.0
64 */
65 public $index = 0;
66
67 /**
68 * Array of field options
69 *
70 * @var array
71 * @since 2.0.0
72 */
73 protected $field_options = array();
74
75 /**
76 * Array of provided field text strings
77 *
78 * @var array
79 * @since 2.0.0
80 */
81 protected $strings;
82
83 /**
84 * The field's render context. In most cases, 'edit', but can be 'display'.
85 *
86 * @var string
87 * @since 2.2.2
88 */
89 public $render_context = 'edit';
90
91 /**
92 * All CMB2_Field callable field arguments.
93 * Can be used to determine if a field argument is callable.
94 *
95 * @var array
96 */
97 public static $callable_fields = array(
98 'default_cb',
99 'classes_cb',
100 'options_cb',
101 'text_cb',
102 'label_cb',
103 'render_row_cb',
104 'display_cb',
105 'before_group',
106 'before_group_row',
107 'before_row',
108 'before',
109 'before_field',
110 'after_field',
111 'after',
112 'after_row',
113 'after_group_row',
114 'after_group',
115 );
116
117 /**
118 * Represents a unique hash representing this field.
119 *
120 * @since 2.2.4
121 *
122 * @var string
123 */
124 protected $hash_id = '';
125
126 /**
127 * Constructs our field object
128 *
129 * @since 1.1.0
130 * @param array $args Field arguments.
131 */
132 public function __construct( $args ) {
133
134 if ( ! empty( $args['group_field'] ) ) {
135 $this->group = $args['group_field'];
136 $this->object_id = $this->group->object_id;
137 $this->object_type = $this->group->object_type;
138 $this->cmb_id = $this->group->cmb_id;
139 } else {
140 $this->object_id = isset( $args['object_id'] ) && '_' !== $args['object_id'] ? $args['object_id'] : 0;
141 $this->object_type = isset( $args['object_type'] ) ? $args['object_type'] : 'post';
142
143 if ( isset( $args['cmb_id'] ) ) {
144 $this->cmb_id = $args['cmb_id'];
145 }
146 }
147
148 $this->args = $this->_set_field_defaults( $args['field_args'] );
149
150 if ( $this->object_id ) {
151 $this->value = $this->get_data();
152 }
153 }
154
155 /**
156 * Non-existent methods fallback to checking for field arguments of the same name
157 *
158 * @since 1.1.0
159 * @param string $name Method name.
160 * @param array $arguments Array of passed-in arguments.
161 * @return mixed Value of field argument
162 */
163 public function __call( $name, $arguments ) {
164 if ( 'string' === $name ) {
165 return call_user_func_array( array( $this, 'get_string' ), $arguments );
166 }
167
168 $key = isset( $arguments[0] ) ? $arguments[0] : '';
169 return $this->args( $name, $key );
170 }
171
172 /**
173 * Retrieves the field id
174 *
175 * @since 1.1.0
176 * @param boolean $raw Whether to retrieve pre-modidifed id.
177 * @return string Field id
178 */
179 public function id( $raw = false ) {
180 $id = $raw ? '_id' : 'id';
181 return $this->args( $id );
182 }
183
184 /**
185 * Get a field argument
186 *
187 * @since 1.1.0
188 * @param string $key Argument to check.
189 * @param string $_key Sub argument to check.
190 * @return mixed Argument value or false if non-existent
191 */
192 public function args( $key = '', $_key = '' ) {
193 $arg = $this->_data( 'args', $key );
194
195 if ( in_array( $key, array( 'default', 'default_cb' ), true ) ) {
196
197 $arg = $this->get_default();
198
199 } elseif ( $_key ) {
200
201 $arg = isset( $arg[ $_key ] ) ? $arg[ $_key ] : false;
202 }
203
204 return $arg;
205 }
206
207 /**
208 * Retrieve a portion of a field property
209 *
210 * @since 1.1.0
211 * @param string $var Field property to check.
212 * @param string $key Field property array key to check.
213 * @return mixed Queried property value or false
214 */
215 public function _data( $var, $key = '' ) {
216 $vars = $this->{$var};
217 if ( $key ) {
218 return array_key_exists( $key, $vars ) ? $vars[ $key ] : false;
219 }
220 return $vars;
221 }
222
223 /**
224 * Get Field's value
225 *
226 * @since 1.1.0
227 * @param string $key If value is an array, is used to get array key->value.
228 * @return mixed Field value or false if non-existent
229 */
230 public function value( $key = '' ) {
231 return $this->_data( 'value', $key );
232 }
233
234 /**
235 * Retrieves metadata/option data
236 *
237 * @since 1.0.1
238 * @param string $field_id Meta key/Option array key.
239 * @param array $args Override arguments.
240 * @return mixed Meta/Option value
241 */
242 public function get_data( $field_id = '', $args = array() ) {
243 if ( $field_id ) {
244 $args['field_id'] = $field_id;
245 } elseif ( $this->group ) {
246 $args['field_id'] = $this->group->id();
247 }
248
249 $a = $this->data_args( $args );
250
251 /**
252 * Filter whether to override getting of meta value.
253 * Returning a non 'cmb2_field_no_override_val' value
254 * will effectively short-circuit the value retrieval.
255 *
256 * @since 2.0.0
257 *
258 * @param mixed $value The value get_metadata() should
259 * return - a single metadata value,
260 * or an array of values.
261 *
262 * @param int $object_id Object ID.
263 *
264 * @param array $args {
265 * An array of arguments for retrieving data
266 *
267 * @type string $type The current object type
268 * @type int $id The current object ID
269 * @type string $field_id The ID of the field being requested
270 * @type bool $repeat Whether current field is repeatable
271 * @type bool $single Whether current field is a single database row
272 * }
273 *
274 * @param CMB2_Field object $field This field object
275 */
276 $data = apply_filters( 'cmb2_override_meta_value', 'cmb2_field_no_override_val', $this->object_id, $a, $this );
277
278 /**
279 * Filter and parameters are documented for 'cmb2_override_meta_value' filter (above).
280 *
281 * The dynamic portion of the hook, $field_id, refers to the current
282 * field id paramater. Returning a non 'cmb2_field_no_override_val' value
283 * will effectively short-circuit the value retrieval.
284 *
285 * @since 2.0.0
286 */
287 $data = apply_filters( "cmb2_override_{$a['field_id']}_meta_value", $data, $this->object_id, $a, $this );
288
289 // If no override, get value normally.
290 if ( 'cmb2_field_no_override_val' === $data ) {
291 $data = 'options-page' === $a['type']
292 ? cmb2_options( $a['id'] )->get( $a['field_id'] )
293 : get_metadata( $a['type'], $a['id'], $a['field_id'], ( $a['single'] || $a['repeat'] ) );
294 }
295
296 if ( $this->group ) {
297
298 $data = is_array( $data ) && isset( $data[ $this->group->index ][ $this->args( '_id' ) ] )
299 ? $data[ $this->group->index ][ $this->args( '_id' ) ]
300 : false;
301 }
302
303 return $data;
304 }
305
306 /**
307 * Updates metadata/option data.
308 *
309 * @since 1.0.1
310 * @param mixed $new_value Value to update data with.
311 * @param bool $single Whether data is an array (add_metadata).
312 * @return mixed
313 */
314 public function update_data( $new_value, $single = true ) {
315 $a = $this->data_args( array(
316 'single' => $single,
317 ) );
318
319 $a['value'] = $a['repeat'] ? array_values( $new_value ) : $new_value;
320
321 /**
322 * Filter whether to override saving of meta value.
323 * Returning a non-null value will effectively short-circuit the function.
324 *
325 * @since 2.0.0
326 *
327 * @param null|bool $check Whether to allow updating metadata for the given type.
328 *
329 * @param array $args {
330 * Array of data about current field including:
331 *
332 * @type string $value The value to set
333 * @type string $type The current object type
334 * @type int $id The current object ID
335 * @type string $field_id The ID of the field being updated
336 * @type bool $repeat Whether current field is repeatable
337 * @type bool $single Whether current field is a single database row
338 * }
339 *
340 * @param array $field_args All field arguments
341 *
342 * @param CMB2_Field object $field This field object
343 */
344 $override = apply_filters( 'cmb2_override_meta_save', null, $a, $this->args(), $this );
345
346 /**
347 * Filter and parameters are documented for 'cmb2_override_meta_save' filter (above).
348 *
349 * The dynamic portion of the hook, $a['field_id'], refers to the current
350 * field id paramater. Returning a non-null value
351 * will effectively short-circuit the function.
352 *
353 * @since 2.0.0
354 */
355 $override = apply_filters( "cmb2_override_{$a['field_id']}_meta_save", $override, $a, $this->args(), $this );
356
357 // If override, return that.
358 if ( null !== $override ) {
359 return $override;
360 }
361
362 // Options page handling (or temp data store).
363 if ( 'options-page' === $a['type'] || empty( $a['id'] ) ) {
364 return cmb2_options( $a['id'] )->update( $a['field_id'], $a['value'], false, $a['single'] );
365 }
366
367 // Add metadata if not single.
368 if ( ! $a['single'] ) {
369 return add_metadata( $a['type'], $a['id'], $a['field_id'], $a['value'], false );
370 }
371
372 // Delete meta if we have an empty array.
373 if ( is_array( $a['value'] ) && empty( $a['value'] ) ) {
374 return delete_metadata( $a['type'], $a['id'], $a['field_id'], $this->value );
375 }
376
377 // Update metadata.
378 return update_metadata( $a['type'], $a['id'], $a['field_id'], $a['value'] );
379 }
380
381 /**
382 * Removes/updates metadata/option data.
383 *
384 * @since 1.0.1
385 * @param string $old Old value.
386 * @return mixed
387 */
388 public function remove_data( $old = '' ) {
389 $a = $this->data_args( array(
390 'old' => $old,
391 ) );
392
393 /**
394 * Filter whether to override removing of meta value.
395 * Returning a non-null value will effectively short-circuit the function.
396 *
397 * @since 2.0.0
398 *
399 * @param null|bool $delete Whether to allow metadata deletion of the given type.
400 * @param array $args Array of data about current field including:
401 * 'type' : Current object type
402 * 'id' : Current object ID
403 * 'field_id' : Current Field ID
404 * 'repeat' : Whether current field is repeatable
405 * 'single' : Whether to save as a
406 * single meta value
407 * @param array $field_args All field arguments
408 * @param CMB2_Field object $field This field object
409 */
410 $override = apply_filters( 'cmb2_override_meta_remove', null, $a, $this->args(), $this );
411
412 /**
413 * Filter whether to override removing of meta value.
414 *
415 * The dynamic portion of the hook, $a['field_id'], refers to the current
416 * field id paramater. Returning a non-null value
417 * will effectively short-circuit the function.
418 *
419 * @since 2.0.0
420 *
421 * @param null|bool $delete Whether to allow metadata deletion of the given type.
422 * @param array $args Array of data about current field including:
423 * 'type' : Current object type
424 * 'id' : Current object ID
425 * 'field_id' : Current Field ID
426 * 'repeat' : Whether current field is repeatable
427 * 'single' : Whether to save as a
428 * single meta value
429 * @param array $field_args All field arguments
430 * @param CMB2_Field object $field This field object
431 */
432 $override = apply_filters( "cmb2_override_{$a['field_id']}_meta_remove", $override, $a, $this->args(), $this );
433
434 // If no override, remove as usual.
435 if ( null !== $override ) {
436 return $override;
437 } // End if.
438 // Option page handling.
439 elseif ( 'options-page' === $a['type'] || empty( $a['id'] ) ) {
440 return cmb2_options( $a['id'] )->remove( $a['field_id'] );
441 }
442
443 // Remove metadata.
444 return delete_metadata( $a['type'], $a['id'], $a['field_id'], $old );
445 }
446
447 /**
448 * Data variables for get/set data methods
449 *
450 * @since 1.1.0
451 * @param array $args Override arguments.
452 * @return array Updated arguments
453 */
454 public function data_args( $args = array() ) {
455 $args = wp_parse_args( $args, array(
456 'type' => $this->object_type,
457 'id' => $this->object_id,
458 'field_id' => $this->id( true ),
459 'repeat' => $this->args( 'repeatable' ),
460 'single' => ! $this->args( 'multiple' ),
461 ) );
462 return $args;
463 }
464
465 /**
466 * Checks if field has a registered sanitization callback
467 *
468 * @since 1.0.1
469 * @param mixed $meta_value Meta value.
470 * @return mixed Possibly sanitized meta value
471 */
472 public function sanitization_cb( $meta_value ) {
473
474 if ( $this->args( 'repeatable' ) && is_array( $meta_value ) ) {
475 // Remove empties.
476 $meta_value = array_filter( $meta_value );
477 }
478
479 // Check if the field has a registered validation callback.
480 $cb = $this->maybe_callback( 'sanitization_cb' );
481 if ( false === $cb ) {
482 // If requesting NO validation, return meta value.
483 return $meta_value;
484 } elseif ( $cb ) {
485 // Ok, callback is good, let's run it.
486 return call_user_func( $cb, $meta_value, $this->args(), $this );
487 }
488
489 $sanitizer = new CMB2_Sanitize( $this, $meta_value );
490 $field_type = $this->type();
491
492 /**
493 * Filter the value before it is saved.
494 *
495 * The dynamic portion of the hook name, $field_type, refers to the field type.
496 *
497 * Passing a non-null value to the filter will short-circuit saving
498 * the field value, saving the passed value instead.
499 *
500 * @param bool|mixed $override_value Sanitization/Validation override value to return.
501 * Default: null. false to skip it.
502 * @param mixed $value The value to be saved to this field.
503 * @param int $object_id The ID of the object where the value will be saved
504 * @param array $field_args The current field's arguments
505 * @param object $sanitizer This `CMB2_Sanitize` object
506 */
507 $override_value = apply_filters( "cmb2_sanitize_{$field_type}", null, $sanitizer->value, $this->object_id, $this->args(), $sanitizer );
508
509 if ( null !== $override_value ) {
510 return $override_value;
511 }
512
513 // Sanitization via 'CMB2_Sanitize'.
514 return $sanitizer->{$field_type}();
515 }
516
517 /**
518 * Process $_POST data to save this field's value
519 *
520 * @since 2.0.3
521 * @param array $data_to_save $_POST data to check.
522 * @return array|int|bool Result of save, false on failure
523 */
524 public function save_field_from_data( array $data_to_save ) {
525 $this->data_to_save = $data_to_save;
526
527 $meta_value = isset( $this->data_to_save[ $this->id( true ) ] )
528 ? $this->data_to_save[ $this->id( true ) ]
529 : null;
530
531 return $this->save_field( $meta_value );
532 }
533
534 /**
535 * Sanitize/store a value to this field
536 *
537 * @since 2.0.0
538 * @param array $meta_value Desired value to sanitize/store.
539 * @return array|int|bool Result of save. false on failure
540 */
541 public function save_field( $meta_value ) {
542
543 $updated = false;
544 $action = '';
545 $new_value = $this->sanitization_cb( $meta_value );
546
547 if ( ! $this->args( 'save_field' ) ) {
548
549 // Nothing to see here.
550 $action = 'disabled';
551
552 } elseif ( $this->args( 'multiple' ) && ! $this->args( 'repeatable' ) && ! $this->group ) {
553
554 $this->remove_data();
555 $count = 0;
556
557 if ( ! empty( $new_value ) ) {
558 foreach ( $new_value as $add_new ) {
559 if ( $this->update_data( $add_new, false ) ) {
560 $count++;
561 }
562 }
563 }
564
565 $updated = $count ? $count : false;
566 $action = 'repeatable';
567
568 } elseif ( ! CMB2_Utils::isempty( $new_value ) && $new_value !== $this->get_data() ) {
569 $updated = $this->update_data( $new_value );
570 $action = 'updated';
571 } elseif ( CMB2_Utils::isempty( $new_value ) ) {
572 $updated = $this->remove_data();
573 $action = 'removed';
574 }
575
576 if ( $updated ) {
577 $this->value = $this->get_data();
578 $this->escaped_value = null;
579 }
580
581 $field_id = $this->id( true );
582
583 /**
584 * Hooks after save field action.
585 *
586 * @since 2.2.0
587 *
588 * @param string $field_id the current field id paramater.
589 * @param bool $updated Whether the metadata update action occurred.
590 * @param string $action Action performed. Could be "repeatable", "updated", or "removed".
591 * @param CMB2_Field object $field This field object
592 */
593 do_action( 'cmb2_save_field', $field_id, $updated, $action, $this );
594
595 /**
596 * Hooks after save field action.
597 *
598 * The dynamic portion of the hook, $field_id, refers to the
599 * current field id paramater.
600 *
601 * @since 2.2.0
602 *
603 * @param bool $updated Whether the metadata update action occurred.
604 * @param string $action Action performed. Could be "repeatable", "updated", or "removed".
605 * @param CMB2_Field object $field This field object
606 */
607 do_action( "cmb2_save_field_{$field_id}", $updated, $action, $this );
608
609 return $updated;
610 }
611
612 /**
613 * Determine if current type is exempt from escaping
614 *
615 * @since 1.1.0
616 * @return bool True if exempt
617 */
618 public function escaping_exception() {
619 // These types cannot be escaped.
620 return in_array( $this->type(), array(
621 'file_list',
622 'multicheck',
623 'text_datetime_timestamp_timezone',
624 ) );
625 }
626
627 /**
628 * Determine if current type cannot be repeatable
629 *
630 * @since 1.1.0
631 * @param string $type Field type to check.
632 * @return bool True if type cannot be repeatable
633 */
634 public function repeatable_exception( $type ) {
635 // These types cannot be repeatable.
636 $internal_fields = array(
637 // Use file_list instead.
638 'file' => 1,
639 'radio' => 1,
640 'title' => 1,
641 'wysiwyg' => 1,
642 'checkbox' => 1,
643 'radio_inline' => 1,
644 'taxonomy_radio' => 1,
645 'taxonomy_radio_inline' => 1,
646 'taxonomy_radio_hierarchical' => 1,
647 'taxonomy_select' => 1,
648 'taxonomy_multicheck' => 1,
649 'taxonomy_multicheck_inline' => 1,
650 'taxonomy_multicheck_hierarchical' => 1,
651
652 );
653
654 /**
655 * Filter field types that are non-repeatable.
656 *
657 * Note that this does *not* allow overriding the default non-repeatable types.
658 *
659 * @since 2.1.1
660 *
661 * @param array $fields Array of fields designated as non-repeatable. Note that the field names are *keys*,
662 * and not values. The value can be anything, because it is meaningless. Example:
663 * array( 'my_custom_field' => 1 )
664 */
665 $all_fields = array_merge( apply_filters( 'cmb2_non_repeatable_fields', array() ), $internal_fields );
666 return isset( $all_fields[ $type ] );
667 }
668
669 /**
670 * Determine if current type has its own defaults field-arguments method.
671 *
672 * @since 2.2.6
673 * @param string $type Field type to check.
674 * @return bool True if has own method.
675 */
676 public function has_args_method( $type ) {
677
678 // These types have their own arguments parser.
679 $type_methods = array(
680 'group' => 'set_field_defaults_group',
681 'wysiwyg' => 'set_field_defaults_wysiwyg',
682 );
683
684 if ( isset( $type_methods[ $type ] ) ) {
685 return $type_methods[ $type ];
686 }
687
688 $all_or_nothing_types = array_flip( apply_filters( 'cmb2_all_or_nothing_types', array(
689 'select',
690 'radio',
691 'radio_inline',
692 'taxonomy_select',
693 'taxonomy_radio',
694 'taxonomy_radio_inline',
695 'taxonomy_radio_hierarchical',
696 ), $this ) );
697
698 if ( isset( $all_or_nothing_types[ $type ] ) ) {
699 return 'set_field_defaults_all_or_nothing_types';
700 }
701
702 return false;
703 }
704
705 /**
706 * Escape the value before output. Defaults to 'esc_attr()'
707 *
708 * @since 1.0.1
709 * @param callable|string $func Escaping function (if not esc_attr()).
710 * @param mixed $meta_value Meta value.
711 * @return mixed Final value.
712 */
713 public function escaped_value( $func = 'esc_attr', $meta_value = '' ) {
714
715 if ( null !== $this->escaped_value ) {
716 return $this->escaped_value;
717 }
718
719 $meta_value = $meta_value ? $meta_value : $this->value();
720
721 // Check if the field has a registered escaping callback.
722 if ( $cb = $this->maybe_callback( 'escape_cb' ) ) {
723 // Ok, callback is good, let's run it.
724 return call_user_func( $cb, $meta_value, $this->args(), $this );
725 }
726
727 $field_type = $this->type();
728
729 /**
730 * Filter the value for escaping before it is ouput.
731 *
732 * The dynamic portion of the hook name, $field_type, refers to the field type.
733 *
734 * Passing a non-null value to the filter will short-circuit the built-in
735 * escaping for this field.
736 *
737 * @param bool|mixed $override_value Escaping override value to return.
738 * Default: null. false to skip it.
739 * @param mixed $meta_value The value to be output.
740 * @param array $field_args The current field's arguments.
741 * @param object $field This `CMB2_Field` object.
742 */
743 $esc = apply_filters( "cmb2_types_esc_{$field_type}", null, $meta_value, $this->args(), $this );
744 if ( null !== $esc ) {
745 return $esc;
746 }
747
748 if ( false === $cb || $this->escaping_exception() ) {
749 // If requesting NO escaping, return meta value.
750 return $this->val_or_default( $meta_value );
751 }
752
753 // escaping function passed in?
754 $func = $func ? $func : 'esc_attr';
755 $meta_value = $this->val_or_default( $meta_value );
756
757 if ( is_array( $meta_value ) ) {
758 foreach ( $meta_value as $key => $value ) {
759 $meta_value[ $key ] = call_user_func( $func, $value );
760 }
761 } else {
762 $meta_value = call_user_func( $func, $meta_value );
763 }
764
765 $this->escaped_value = $meta_value;
766 return $this->escaped_value;
767 }
768
769 /**
770 * Return non-empty value or field default if value IS empty
771 *
772 * @since 2.0.0
773 * @param mixed $meta_value Field value.
774 * @return mixed Field value, or default value
775 */
776 public function val_or_default( $meta_value ) {
777 return ! CMB2_Utils::isempty( $meta_value ) ? $meta_value : $this->get_default();
778 }
779
780 /**
781 * Offset a time value based on timezone
782 *
783 * @since 1.0.0
784 * @return string Offset time string
785 */
786 public function field_timezone_offset() {
787 return CMB2_Utils::timezone_offset( $this->field_timezone() );
788 }
789
790 /**
791 * Return timezone string
792 *
793 * @since 1.0.0
794 * @return string Timezone string
795 */
796 public function field_timezone() {
797 $value = '';
798
799 // Is timezone arg set?
800 if ( $this->args( 'timezone' ) ) {
801 $value = $this->args( 'timezone' );
802 } // End if.
803 // Is there another meta key with a timezone stored as its value we should use?
804 elseif ( $this->args( 'timezone_meta_key' ) ) {
805 $value = $this->get_data( $this->args( 'timezone_meta_key' ) );
806 }
807
808 return $value;
809 }
810
811 /**
812 * Format the timestamp field value based on the field date/time format arg
813 *
814 * @since 2.0.0
815 * @param int $meta_value Timestamp.
816 * @param string $format Either date_format or time_format.
817 * @return string Formatted date
818 */
819 public function format_timestamp( $meta_value, $format = 'date_format' ) {
820 return date( stripslashes( $this->args( $format ) ), $meta_value );
821 }
822
823 /**
824 * Return a formatted timestamp for a field
825 *
826 * @since 2.0.0
827 * @param string $format Either date_format or time_format.
828 * @param string|int $meta_value Optional meta value to check.
829 * @return string Formatted date
830 */
831 public function get_timestamp_format( $format = 'date_format', $meta_value = 0 ) {
832 $meta_value = $meta_value ? $meta_value : $this->escaped_value();
833 $meta_value = CMB2_Utils::make_valid_time_stamp( $meta_value );
834
835 if ( empty( $meta_value ) ) {
836 return '';
837 }
838
839 return is_array( $meta_value )
840 ? array_map( array( $this, 'format_timestamp' ), $meta_value, $format )
841 : $this->format_timestamp( $meta_value, $format );
842 }
843
844 /**
845 * Get timestamp from text date
846 *
847 * @since 2.2.0
848 * @param string $value Date value.
849 * @return mixed Unix timestamp representing the date.
850 */
851 public function get_timestamp_from_value( $value ) {
852 return CMB2_Utils::get_timestamp_from_value( $value, $this->args( 'date_format' ) );
853 }
854
855 /**
856 * Get field render callback and Render the field row
857 *
858 * @since 1.0.0
859 */
860 public function render_field() {
861 $this->render_context = 'edit';
862
863 $this->peform_param_callback( 'render_row_cb' );
864
865 // For chaining.
866 return $this;
867 }
868
869 /**
870 * Default field render callback
871 *
872 * @since 2.1.1
873 */
874 public function render_field_callback() {
875
876 // If field is requesting to not be shown on the front-end.
877 if ( ! is_admin() && ! $this->args( 'on_front' ) ) {
878 return;
879 }
880
881 // If field is requesting to be conditionally shown.
882 if ( ! $this->should_show() ) {
883 return;
884 }
885
886 $this->peform_param_callback( 'before_row' );
887
888 printf( "<div class=\"cmb-row %s\" data-fieldtype=\"%s\">\n", $this->row_classes(), $this->type() );
889
890 if ( ! $this->args( 'show_names' ) ) {
891 echo "\n\t<div class=\"cmb-td\">\n";
892
893 $this->peform_param_callback( 'label_cb' );
894
895 } else {
896
897 if ( $this->get_param_callback_result( 'label_cb' ) ) {
898 echo '<div class="cmb-th">', $this->peform_param_callback( 'label_cb' ), '</div>';
899 }
900
901 echo "\n\t<div class=\"cmb-td\">\n";
902 }
903
904 $this->peform_param_callback( 'before' );
905
906 $types = new CMB2_Types( $this );
907 $types->render();
908
909 $this->peform_param_callback( 'after' );
910
911 echo "\n\t</div>\n</div>";
912
913 $this->peform_param_callback( 'after_row' );
914
915 // For chaining.
916 return $this;
917 }
918
919 /**
920 * The default label_cb callback (if not a title field)
921 *
922 * @since 2.1.1
923 * @return string Label html markup.
924 */
925 public function label() {
926 if ( ! $this->args( 'name' ) ) {
927 return '';
928 }
929
930 $style = ! $this->args( 'show_names' ) ? ' style="display:none;"' : '';
931
932 return sprintf( "\n" . '<label%1$s for="%2$s">%3$s</label>' . "\n", $style, $this->id(), $this->args( 'name' ) );
933 }
934
935 /**
936 * Defines the classes for the current CMB2 field row
937 *
938 * @since 2.0.0
939 * @return string Space concatenated list of classes
940 */
941 public function row_classes() {
942
943 $classes = array();
944
945 /**
946 * By default, 'text_url' and 'text' fields get table-like styling
947 *
948 * @since 2.0.0
949 *
950 * @param array $field_types The types of fields which should get the 'table-layout' class
951 */
952 $repeat_table_rows_types = apply_filters( 'cmb2_repeat_table_row_types', array(
953 'text_url',
954 'text',
955 ) );
956
957 $conditional_classes = array(
958 'cmb-type-' . str_replace( '_', '-', sanitize_html_class( $this->type() ) ) => true,
959 'cmb2-id-' . str_replace( '_', '-', sanitize_html_class( $this->id() ) ) => true,
960 'cmb-repeat' => $this->args( 'repeatable' ),
961 'cmb-repeat-group-field' => $this->group,
962 'cmb-inline' => $this->args( 'inline' ),
963 'table-layout' => 'edit' === $this->render_context && in_array( $this->type(), $repeat_table_rows_types ),
964 );
965
966 foreach ( $conditional_classes as $class => $condition ) {
967 if ( $condition ) {
968 $classes[] = $class;
969 }
970 }
971
972 if ( $added_classes = $this->args( 'classes' ) ) {
973 $added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes;
974 } elseif ( $added_classes = $this->get_param_callback_result( 'classes_cb' ) ) {
975 $added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes;
976 }
977
978 if ( $added_classes ) {
979 $classes[] = esc_attr( $added_classes );
980 }
981
982 /**
983 * Globally filter row classes
984 *
985 * @since 2.0.0
986 *
987 * @param string $classes Space-separated list of row classes
988 * @param CMB2_Field object $field This field object
989 */
990 return apply_filters( 'cmb2_row_classes', implode( ' ', $classes ), $this );
991 }
992
993 /**
994 * Get field display callback and render the display value in the column.
995 *
996 * @since 2.2.2
997 */
998 public function render_column() {
999 $this->render_context = 'display';
1000
1001 $this->peform_param_callback( 'display_cb' );
1002
1003 // For chaining.
1004 return $this;
1005 }
1006
1007 /**
1008 * The method to fetch the value for this field for the REST API.
1009 *
1010 * @since 2.5.0
1011 */
1012 public function get_rest_value() {
1013 $field_type = $this->type();
1014 $field_id = $this->id( true );
1015
1016 if ( $cb = $this->maybe_callback( 'rest_value_cb' ) ) {
1017 apply_filters( "cmb2_get_rest_value_for_{$field_id}", $cb, 99 );
1018 }
1019
1020 $value = $this->get_data();
1021
1022 /**
1023 * Filter the value before it is sent to the REST request.
1024 *
1025 * @since 2.5.0
1026 *
1027 * @param mixed $value The value from CMB2_Field::get_data()
1028 * @param CMB2_Field $field This field object.
1029 */
1030 $value = apply_filters( 'cmb2_get_rest_value', $value, $this );
1031
1032 /**
1033 * Filter the value before it is sent to the REST request.
1034 *
1035 * The dynamic portion of the hook name, $field_type, refers to the field type.
1036 *
1037 * @since 2.5.0
1038 *
1039 * @param mixed $value The value from CMB2_Field::get_data()
1040 * @param CMB2_Field $field This field object.
1041 */
1042 $value = apply_filters( "cmb2_get_rest_value_{$field_type}", $value, $this );
1043
1044 /**
1045 * Filter the value before it is sent to the REST request.
1046 *
1047 * The dynamic portion of the hook name, $field_id, refers to the field id.
1048 *
1049 * @since 2.5.0
1050 *
1051 * @param mixed $value The value from CMB2_Field::get_data()
1052 * @param CMB2_Field $field This field object.
1053 */
1054 return apply_filters( "cmb2_get_rest_value_for_{$field_id}", $value, $this );
1055 }
1056
1057 /**
1058 * Default callback to outputs field value in a display format.
1059 *
1060 * @since 2.2.2
1061 */
1062 public function display_value_callback() {
1063 // If field is requesting to be conditionally shown.
1064 if ( ! $this->should_show() ) {
1065 return;
1066 }
1067
1068 $display = new CMB2_Field_Display( $this );
1069 $field_type = $this->type();
1070
1071 /**
1072 * A filter to bypass the default display.
1073 *
1074 * The dynamic portion of the hook name, $field_type, refers to the field type.
1075 *
1076 * Passing a non-null value to the filter will short-circuit the default display.
1077 *
1078 * @param bool|mixed $pre_output Default null value.
1079 * @param CMB2_Field $field This field object.
1080 * @param CMB2_Field_Display $display The `CMB2_Field_Display` object.
1081 */
1082 $pre_output = apply_filters( "cmb2_pre_field_display_{$field_type}", null, $this, $display );
1083
1084 if ( null !== $pre_output ) {
1085 echo $pre_output;
1086 return;
1087 }
1088
1089 $this->peform_param_callback( 'before_display_wrap' );
1090
1091 printf( "<div class=\"cmb-column %s\" data-fieldtype=\"%s\">\n", $this->row_classes(), $field_type );
1092
1093 $this->peform_param_callback( 'before_display' );
1094
1095 CMB2_Field_Display::get( $this )->display();
1096
1097 $this->peform_param_callback( 'after_display' );
1098
1099 echo "\n</div>";
1100
1101 $this->peform_param_callback( 'after_display_wrap' );
1102
1103 // For chaining.
1104 return $this;
1105 }
1106
1107 /**
1108 * Replaces a hash key - {#} - with the repeatable index
1109 *
1110 * @since 1.2.0
1111 * @param string $value Value to update.
1112 * @return string Updated value
1113 */
1114 public function replace_hash( $value ) {
1115 // Replace hash with 1 based count.
1116 return str_replace( '{#}', ( $this->index + 1 ), $value );
1117 }
1118
1119 /**
1120 * Retrieve text parameter from field's text array (if it has one), or use fallback text
1121 * For back-compatibility, falls back to checking the options array.
1122 *
1123 * @since 2.2.2
1124 * @param string $text_key Key in field's text array.
1125 * @param string $fallback Fallback text.
1126 * @return string Text
1127 */
1128 public function get_string( $text_key, $fallback ) {
1129 // If null, populate with our field strings values.
1130 if ( null === $this->strings ) {
1131 $this->strings = (array) $this->args['text'];
1132
1133 if ( is_callable( $this->args['text_cb'] ) ) {
1134 $strings = call_user_func( $this->args['text_cb'], $this );
1135
1136 if ( $strings && is_array( $strings ) ) {
1137 $this->strings += $strings;
1138 }
1139 }
1140 }
1141
1142 // If we have that string value, send it back.
1143 if ( isset( $this->strings[ $text_key ] ) ) {
1144 return $this->strings[ $text_key ];
1145 }
1146
1147 // Check options for back-compat.
1148 $string = $this->options( $text_key );
1149
1150 return $string ? $string : $fallback;
1151 }
1152
1153 /**
1154 * Retrieve options args.
1155 *
1156 * @since 2.0.0
1157 * @param string $key Specific option to retrieve.
1158 * @return array|mixed Array of options or specific option.
1159 */
1160 public function options( $key = '' ) {
1161 if ( empty( $this->field_options ) ) {
1162 $this->set_options();
1163 }
1164
1165 if ( $key ) {
1166 return array_key_exists( $key, $this->field_options ) ? $this->field_options[ $key ] : false;
1167 }
1168
1169 return $this->field_options;
1170 }
1171
1172 /**
1173 * Generates/sets options args. Calls options_cb if it exists.
1174 *
1175 * @since 2.2.5
1176 *
1177 * @return array Array of options
1178 */
1179 public function set_options() {
1180 $this->field_options = (array) $this->args['options'];
1181
1182 if ( is_callable( $this->args['options_cb'] ) ) {
1183 $options = call_user_func( $this->args['options_cb'], $this );
1184
1185 if ( $options && is_array( $options ) ) {
1186 $this->field_options = $options + $this->field_options;
1187 }
1188 }
1189
1190 return $this->field_options;
1191 }
1192
1193 /**
1194 * Store JS dependencies as part of the field args.
1195 *
1196 * @since 2.2.0
1197 * @param array $dependencies Dependies to register for this field.
1198 */
1199 public function add_js_dependencies( $dependencies = array() ) {
1200 foreach ( (array) $dependencies as $dependency ) {
1201 $this->args['js_dependencies'][ $dependency ] = $dependency;
1202 }
1203
1204 CMB2_JS::add_dependencies( $dependencies );
1205 }
1206
1207 /**
1208 * Send field data to JS.
1209 *
1210 * @since 2.2.0
1211 */
1212 public function register_js_data() {
1213 if ( $this->group ) {
1214 CMB2_JS::add_field_data( $this->group );
1215 }
1216
1217 return CMB2_JS::add_field_data( $this );
1218 }
1219
1220 /**
1221 * Get an array of some of the field data to be used in the Javascript.
1222 *
1223 * @since 2.2.4
1224 *
1225 * @return array
1226 */
1227 public function js_data() {
1228 return array(
1229 'label' => $this->args( 'name' ),
1230 'id' => $this->id( true ),
1231 'type' => $this->type(),
1232 'hash' => $this->hash_id(),
1233 'box' => $this->cmb_id,
1234 'id_attr' => $this->id(),
1235 'name_attr' => $this->args( '_name' ),
1236 'default' => $this->get_default(),
1237 'group' => $this->group_id(),
1238 'index' => $this->group ? $this->group->index : null,
1239 );
1240 }
1241
1242 /**
1243 * Returns a unique hash representing this field.
1244 *
1245 * @since 2.2.4
1246 *
1247 * @return string
1248 */
1249 public function hash_id() {
1250 if ( '' === $this->hash_id ) {
1251 $this->hash_id = CMB2_Utils::generate_hash( $this->cmb_id . '||' . $this->id() );
1252 }
1253
1254 return $this->hash_id;
1255 }
1256
1257 /**
1258 * Gets the id of the group field if this field is part of a group.
1259 *
1260 * @since 2.2.4
1261 *
1262 * @return string
1263 */
1264 public function group_id() {
1265 return $this->group ? $this->group->id( true ) : '';
1266 }
1267
1268 /**
1269 * Get CMB2_Field default value, either from default param or default_cb param.
1270 *
1271 * @since 0.2.2
1272 *
1273 * @return mixed Default field value
1274 */
1275 public function get_default() {
1276 $default = $this->args['default'];
1277
1278 if ( null !== $default ) {
1279 return apply_filters( 'cmb2_default_filter', $default, $this );
1280 }
1281
1282 $param = is_callable( $this->args['default_cb'] ) ? 'default_cb' : 'default';
1283 $default = $this->args['default'] = $this->get_param_callback_result( $param );
1284
1285 // Allow a filter override of the default value.
1286 return apply_filters( 'cmb2_default_filter', $this->args['default'], $this );
1287 }
1288
1289 /**
1290 * Fills in empty field parameters with defaults
1291 *
1292 * @since 1.1.0
1293 *
1294 * @param array $args Field config array.
1295 * @return array Modified field config array.
1296 */
1297 public function _set_field_defaults( $args ) {
1298
1299 // Set up blank or default values for empty ones.
1300 $args = wp_parse_args( $args, $this->get_default_field_args( $args ) );
1301
1302 /*
1303 * Deprecated usage:
1304 *
1305 * 'std' -- use 'default' (no longer works)
1306 * 'row_classes' -- use 'class', or 'class_cb'
1307 * 'default' -- as callback (use default_cb)
1308 */
1309 $args = $this->convert_deprecated_params( $args );
1310
1311 $args['repeatable'] = $args['repeatable'] && ! $this->repeatable_exception( $args['type'] );
1312 $args['inline'] = $args['inline'] || false !== stripos( $args['type'], '_inline' );
1313 $args['_id'] = $args['id'];
1314 $args['_name'] = $args['id'];
1315
1316 if ( $method = $this->has_args_method( $args['type'] ) ) {
1317 $args = $this->{$method}( $args );
1318 }
1319
1320 if ( $this->group ) {
1321 $args = $this->set_group_sub_field_defaults( $args );
1322 }
1323
1324 $args['has_supporting_data'] = in_array(
1325 $args['type'],
1326 array(
1327 // CMB2_Sanitize::_save_file_id_value()/CMB2_Sanitize::_get_group_file_value_array().
1328 'file',
1329 // See CMB2_Sanitize::_save_utc_value().
1330 'text_datetime_timestamp_timezone',
1331 ),
1332 true
1333 );
1334
1335 return $args;
1336 }
1337
1338 /**
1339 * Sets default arguments for the group field types.
1340 *
1341 * @since 2.2.6
1342 *
1343 * @param array $args Field config array.
1344 * @return array Modified field config array.
1345 */
1346 protected function set_field_defaults_group( $args ) {
1347 $args['options'] = wp_parse_args( $args['options'], array(
1348 'add_button' => esc_html__( 'Add Group', 'cmb2' ),
1349 'remove_button' => esc_html__( 'Remove Group', 'cmb2' ),
1350 ) );
1351
1352 return $args;
1353 }
1354
1355 /**
1356 * Sets default arguments for the wysiwyg field types.
1357 *
1358 * @since 2.2.6
1359 *
1360 * @param array $args Field config array.
1361 * @return array Modified field config array.
1362 */
1363 protected function set_field_defaults_wysiwyg( $args ) {
1364 $args['id'] = strtolower( str_ireplace( '-', '_', $args['id'] ) );
1365 $args['options']['textarea_name'] = $args['_name'];
1366
1367 return $args;
1368 }
1369
1370 /**
1371 * Sets default arguments for the all-or-nothing field types.
1372 *
1373 * @since 2.2.6
1374 *
1375 * @param array $args Field config array.
1376 * @return array Modified field config array.
1377 */
1378 protected function set_field_defaults_all_or_nothing_types( $args ) {
1379 $args['show_option_none'] = isset( $args['show_option_none'] ) ? $args['show_option_none'] : null;
1380 $args['show_option_none'] = true === $args['show_option_none'] ? esc_html__( 'None', 'cmb2' ) : $args['show_option_none'];
1381
1382 if ( null === $args['show_option_none'] ) {
1383 $off_by_default = in_array( $args['type'], array( 'select', 'radio', 'radio_inline' ), true );
1384 $args['show_option_none'] = $off_by_default ? false : esc_html__( 'None', 'cmb2' );
1385 }
1386
1387 return $args;
1388 }
1389
1390 /**
1391 * Sets default arguments for group sub-fields.
1392 *
1393 * @since 2.2.6
1394 *
1395 * @param array $args Field config array.
1396 * @return array Modified field config array.
1397 */
1398 protected function set_group_sub_field_defaults( $args ) {
1399 $args['id'] = $this->group->args( 'id' ) . '_' . $this->group->index . '_' . $args['id'];
1400 $args['_name'] = $this->group->args( 'id' ) . '[' . $this->group->index . '][' . $args['_name'] . ']';
1401
1402 return $args;
1403 }
1404
1405 /**
1406 * Gets the default arguments for all fields.
1407 *
1408 * @since 2.2.6
1409 *
1410 * @param array $args Field config array.
1411 * @return array Field defaults.
1412 */
1413 protected function get_default_field_args( $args ) {
1414 $type = isset( $args['type'] ) ? $args['type'] : '';
1415
1416 return array(
1417 'type' => $type,
1418 'name' => '',
1419 'desc' => '',
1420 'before' => '',
1421 'after' => '',
1422 'options' => array(),
1423 'options_cb' => '',
1424 'text' => array(),
1425 'text_cb' => '',
1426 'attributes' => array(),
1427 'protocols' => null,
1428 'default' => null,
1429 'default_cb' => '',
1430 'classes' => null,
1431 'classes_cb' => '',
1432 'select_all_button' => true,
1433 'multiple' => false,
1434 'repeatable' => 'group' === $type,
1435 'inline' => false,
1436 'on_front' => true,
1437 'show_names' => true,
1438 'save_field' => true, // Will not save if false.
1439 'date_format' => 'm\/d\/Y',
1440 'time_format' => 'h:i A',
1441 'description' => isset( $args['desc'] ) ? $args['desc'] : '',
1442 'preview_size' => 'file' === $type ? array( 350, 350 ) : array( 50, 50 ),
1443 'render_row_cb' => array( $this, 'render_field_callback' ),
1444 'display_cb' => array( $this, 'display_value_callback' ),
1445 'label_cb' => 'title' !== $type ? array( $this, 'label' ) : '',
1446 'column' => false,
1447 'js_dependencies' => array(),
1448 'show_in_rest' => null,
1449 );
1450 }
1451
1452 /**
1453 * Get default field arguments specific to this CMB2 object.
1454 *
1455 * @since 2.2.0
1456 * @param array $field_args Metabox field config array.
1457 * @param CMB2_Field $field_group (optional) CMB2_Field object (group parent).
1458 * @return array Array of field arguments.
1459 */
1460 protected function get_default_args( $field_args, $field_group = null ) {
1461 $args = parent::get_default_args( array(), $this->group );
1462
1463 if ( isset( $field_args['field_args'] ) ) {
1464 $args = wp_parse_args( $field_args, $args );
1465 } else {
1466 $args['field_args'] = wp_parse_args( $field_args, $this->args );
1467 }
1468
1469 return $args;
1470 }
1471
1472 /**
1473 * Returns a cloned version of this field object, but with
1474 * modified/overridden field arguments.
1475 *
1476 * @since 2.2.2
1477 * @param array $field_args Array of field arguments, or entire array of
1478 * arguments for CMB2_Field.
1479 *
1480 * @return CMB2_Field The new CMB2_Field instance.
1481 */
1482 public function get_field_clone( $field_args ) {
1483 return $this->get_new_field( $field_args );
1484 }
1485
1486 /**
1487 * Returns the CMB2 instance this field is registered to.
1488 *
1489 * @since 2.2.2
1490 *
1491 * @return CMB2|WP_Error If new CMB2_Field is called without cmb_id arg, returns error.
1492 */
1493 public function get_cmb() {
1494 if ( ! $this->cmb_id ) {
1495 return new WP_Error( 'no_cmb_id', esc_html__( 'Sorry, this field does not have a cmb_id specified.', 'cmb2' ) );
1496 }
1497
1498 return cmb2_get_metabox( $this->cmb_id, $this->object_id, $this->object_type );
1499 }
1500
1501 /**
1502 * Converts deprecated field parameters to the current/proper parameter, and throws a deprecation notice.
1503 *
1504 * @since 2.2.3
1505 * @param array $args Metabox field config array.
1506 * @return array Modified field config array.
1507 */
1508 protected function convert_deprecated_params( $args ) {
1509
1510 if ( isset( $args['row_classes'] ) ) {
1511
1512 // We'll let this one be.
1513 // $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_PARAM, 'row_classes', 'classes' );
1514 // row_classes param could be a callback. This is definitely deprecated.
1515 if ( is_callable( $args['row_classes'] ) ) {
1516
1517 $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'row_classes', 'classes_cb' );
1518
1519 $args['classes_cb'] = $args['row_classes'];
1520 $args['classes'] = null;
1521 } else {
1522
1523 $args['classes'] = $args['row_classes'];
1524 }
1525
1526 unset( $args['row_classes'] );
1527 }
1528
1529 // default param can be passed a callback as well.
1530 if ( is_callable( $args['default'] ) ) {
1531
1532 $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'default', 'default_cb' );
1533
1534 $args['default_cb'] = $args['default'];
1535 $args['default'] = null;
1536 }
1537
1538 // options param can be passed a callback as well.
1539 if ( is_callable( $args['options'] ) ) {
1540
1541 $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'options', 'options_cb' );
1542
1543 $args['options_cb'] = $args['options'];
1544 $args['options'] = array();
1545 }
1546
1547 return $args;
1548 }
1549
1550 }
1551