Passed
Push — add/43 ( b57d71...424871 )
by Chris
14:00 queued 07:35
created

CMB2_Field   F

Complexity

Total Complexity 177

Size/Duplication

Total Lines 1625
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 466
c 1
b 0
f 0
dl 0
loc 1625
rs 2
wmc 177

How to fix   Complexity   

Complex Class

Complex classes like CMB2_Field often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CMB2_Field, and based on these observations, apply Extract Interface, too.

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_select_hierarchical'     => 1,
649
			'taxonomy_multicheck'              => 1,
650
			'taxonomy_multicheck_inline'       => 1,
651
			'taxonomy_multicheck_hierarchical' => 1,
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_select_hierarchical',
694
			'taxonomy_radio',
695
			'taxonomy_radio_inline',
696
			'taxonomy_radio_hierarchical',
697
		), $this ) );
698
699
		if ( isset( $all_or_nothing_types[ $type ] ) ) {
700
			return 'set_field_defaults_all_or_nothing_types';
701
		}
702
703
		return false;
704
	}
705
706
	/**
707
	 * Escape the value before output. Defaults to 'esc_attr()'
708
	 *
709
	 * @since  1.0.1
710
	 * @param  callable|string $func       Escaping function (if not esc_attr()).
711
	 * @param  mixed           $meta_value Meta value.
712
	 * @return mixed                Final value.
713
	 */
714
	public function escaped_value( $func = 'esc_attr', $meta_value = '' ) {
715
716
		if ( null !== $this->escaped_value ) {
717
			return $this->escaped_value;
718
		}
719
720
		$meta_value = $meta_value ? $meta_value : $this->value();
721
722
		// Check if the field has a registered escaping callback.
723
		if ( $cb = $this->maybe_callback( 'escape_cb' ) ) {
724
			// Ok, callback is good, let's run it.
725
			return call_user_func( $cb, $meta_value, $this->args(), $this );
726
		}
727
728
		$field_type = $this->type();
729
730
		/**
731
		 * Filter the value for escaping before it is ouput.
732
		 *
733
		 * The dynamic portion of the hook name, $field_type, refers to the field type.
734
		 *
735
		 * Passing a non-null value to the filter will short-circuit the built-in
736
		 * escaping for this field.
737
		 *
738
		 * @param bool|mixed $override_value Escaping override value to return.
739
		 *                                   Default: null. false to skip it.
740
		 * @param mixed      $meta_value The value to be output.
741
		 * @param array      $field_args The current field's arguments.
742
		 * @param object     $field      This `CMB2_Field` object.
743
		 */
744
		$esc = apply_filters( "cmb2_types_esc_{$field_type}", null, $meta_value, $this->args(), $this );
745
		if ( null !== $esc ) {
746
			return $esc;
747
		}
748
749
		if ( false === $cb || $this->escaping_exception() ) {
750
			// If requesting NO escaping, return meta value.
751
			return $this->val_or_default( $meta_value );
752
		}
753
754
		// escaping function passed in?
755
		$func       = $func ? $func : 'esc_attr';
756
		$meta_value = $this->val_or_default( $meta_value );
757
758
		if ( is_array( $meta_value ) ) {
759
			foreach ( $meta_value as $key => $value ) {
760
				$meta_value[ $key ] = call_user_func( $func, $value );
761
			}
762
		} else {
763
			$meta_value = call_user_func( $func, $meta_value );
764
		}
765
766
		$this->escaped_value = $meta_value;
767
		return $this->escaped_value;
768
	}
769
770
	/**
771
	 * Return non-empty value or field default if value IS empty
772
	 *
773
	 * @since  2.0.0
774
	 * @param  mixed $meta_value Field value.
775
	 * @return mixed             Field value, or default value
776
	 */
777
	public function val_or_default( $meta_value ) {
778
		return ! CMB2_Utils::isempty( $meta_value ) ? $meta_value : $this->get_default();
779
	}
780
781
	/**
782
	 * Offset a time value based on timezone
783
	 *
784
	 * @since  1.0.0
785
	 * @return string Offset time string
786
	 */
787
	public function field_timezone_offset() {
788
		return CMB2_Utils::timezone_offset( $this->field_timezone() );
789
	}
790
791
	/**
792
	 * Return timezone string
793
	 *
794
	 * @since  1.0.0
795
	 * @return string Timezone string
796
	 */
797
	public function field_timezone() {
798
		$value = '';
799
800
		// Is timezone arg set?
801
		if ( $this->args( 'timezone' ) ) {
802
			$value = $this->args( 'timezone' );
803
		} // End if.
804
		// Is there another meta key with a timezone stored as its value we should use?
805
		elseif ( $this->args( 'timezone_meta_key' ) ) {
806
			$value = $this->get_data( $this->args( 'timezone_meta_key' ) );
807
		}
808
809
		return $value;
810
	}
811
812
	/**
813
	 * Format the timestamp field value based on the field date/time format arg
814
	 *
815
	 * @since  2.0.0
816
	 * @param  int    $meta_value Timestamp.
817
	 * @param  string $format     Either date_format or time_format.
818
	 * @return string             Formatted date
819
	 */
820
	public function format_timestamp( $meta_value, $format = 'date_format' ) {
821
		return date( stripslashes( $this->args( $format ) ), $meta_value );
822
	}
823
824
	/**
825
	 * Return a formatted timestamp for a field
826
	 *
827
	 * @since  2.0.0
828
	 * @param  string     $format     Either date_format or time_format.
829
	 * @param  string|int $meta_value Optional meta value to check.
830
	 * @return string             Formatted date
831
	 */
832
	public function get_timestamp_format( $format = 'date_format', $meta_value = 0 ) {
833
		$meta_value = $meta_value ? $meta_value : $this->escaped_value();
834
		if ( empty( $meta_value ) ) {
835
			$meta_value = $this->get_default();
836
		}
837
838
		$meta_value = CMB2_Utils::make_valid_time_stamp( $meta_value );
839
		if ( empty( $meta_value ) ) {
840
			return '';
841
		}
842
843
		return is_array( $meta_value )
844
			? array_map( array( $this, 'format_timestamp' ), $meta_value, $format )
845
			: $this->format_timestamp( $meta_value, $format );
846
	}
847
848
	/**
849
	 * Get timestamp from text date
850
	 *
851
	 * @since  2.2.0
852
	 * @param  string $value Date value.
853
	 * @return mixed         Unix timestamp representing the date.
854
	 */
855
	public function get_timestamp_from_value( $value ) {
856
		return CMB2_Utils::get_timestamp_from_value( $value, $this->args( 'date_format' ) );
857
	}
858
859
	/**
860
	 * Get field render callback and Render the field row
861
	 *
862
	 * @since 1.0.0
863
	 */
864
	public function render_field() {
865
		$this->render_context = 'edit';
866
867
		$this->peform_param_callback( 'render_row_cb' );
868
869
		// For chaining.
870
		return $this;
871
	}
872
873
	/**
874
	 * Default field render callback
875
	 *
876
	 * @since 2.1.1
877
	 */
878
	public function render_field_callback() {
879
880
		// If field is requesting to not be shown on the front-end.
881
		if ( ! is_admin() && ! $this->args( 'on_front' ) ) {
882
			return;
883
		}
884
885
		// If field is requesting to be conditionally shown.
886
		if ( ! $this->should_show() ) {
887
			return;
888
		}
889
890
		$field_type = $this->type();
891
892
		/**
893
		 * Hook before field row begins.
894
		 *
895
		 * @param CMB2_Field $field The current field object.
896
		 */
897
		do_action( 'cmb2_before_field_row', $this );
898
899
		/**
900
		 * Hook before field row begins.
901
		 *
902
		 * The dynamic portion of the hook name, $field_type, refers to the field type.
903
		 *
904
		 * @param CMB2_Field $field The current field object.
905
		 */
906
		do_action( "cmb2_before_{$field_type}_field_row", $this );
907
908
		$this->peform_param_callback( 'before_row' );
909
910
		printf( "<div class=\"cmb-row %s\" data-fieldtype=\"%s\">\n", $this->row_classes(), $field_type );
911
912
		if ( ! $this->args( 'show_names' ) ) {
913
			echo "\n\t<div class=\"cmb-td\">\n";
914
915
			$this->peform_param_callback( 'label_cb' );
916
917
		} else {
918
919
			if ( $this->get_param_callback_result( 'label_cb' ) ) {
920
				echo '<div class="cmb-th">', $this->peform_param_callback( 'label_cb' ), '</div>';
921
			}
922
923
			echo "\n\t<div class=\"cmb-td\">\n";
924
		}
925
926
		$this->peform_param_callback( 'before' );
927
928
		$types = new CMB2_Types( $this );
929
		$types->render();
930
931
		$this->peform_param_callback( 'after' );
932
933
		echo "\n\t</div>\n</div>";
934
935
		$this->peform_param_callback( 'after_row' );
936
937
		/**
938
		 * Hook after field row ends.
939
		 *
940
		 * The dynamic portion of the hook name, $field_type, refers to the field type.
941
		 *
942
		 * @param CMB2_Field $field The current field object.
943
		 */
944
		do_action( "cmb2_after_{$field_type}_field_row", $this );
945
946
		/**
947
		 * Hook after field row ends.
948
		 *
949
		 * @param CMB2_Field $field The current field object.
950
		 */
951
		do_action( 'cmb2_after_field_row', $this );
952
953
		// For chaining.
954
		return $this;
955
	}
956
957
	/**
958
	 * The default label_cb callback (if not a title field)
959
	 *
960
	 * @since  2.1.1
961
	 * @return string Label html markup.
962
	 */
963
	public function label() {
964
		if ( ! $this->args( 'name' ) ) {
965
			return '';
966
		}
967
968
		$style = ! $this->args( 'show_names' ) ? ' style="display:none;"' : '';
969
970
		return sprintf( "\n" . '<label%1$s for="%2$s">%3$s</label>' . "\n", $style, $this->id(), $this->args( 'name' ) );
971
	}
972
973
	/**
974
	 * Defines the classes for the current CMB2 field row
975
	 *
976
	 * @since  2.0.0
977
	 * @return string Space concatenated list of classes
978
	 */
979
	public function row_classes() {
980
981
		$classes = array();
982
983
		/**
984
		 * By default, 'text_url' and 'text' fields get table-like styling
985
		 *
986
		 * @since 2.0.0
987
		 *
988
		 * @param array $field_types The types of fields which should get the 'table-layout' class
989
		 */
990
		$repeat_table_rows_types = apply_filters( 'cmb2_repeat_table_row_types', array(
991
			'text_url',
992
			'text',
993
		) );
994
995
		$conditional_classes = array(
996
			'cmb-type-' . str_replace( '_', '-', sanitize_html_class( $this->type() ) ) => true,
997
			'cmb2-id-' . str_replace( '_', '-', sanitize_html_class( $this->id() ) )    => true,
998
			'cmb-repeat'             => $this->args( 'repeatable' ),
999
			'cmb-repeat-group-field' => $this->group,
1000
			'cmb-inline'             => $this->args( 'inline' ),
1001
			'table-layout'           => 'edit' === $this->render_context && in_array( $this->type(), $repeat_table_rows_types ),
1002
		);
1003
1004
		foreach ( $conditional_classes as $class => $condition ) {
1005
			if ( $condition ) {
1006
				$classes[] = $class;
1007
			}
1008
		}
1009
1010
		if ( $added_classes = $this->args( 'classes' ) ) {
1011
			$added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes;
1012
		} elseif ( $added_classes = $this->get_param_callback_result( 'classes_cb' ) ) {
1013
			$added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes;
1014
		}
1015
1016
		if ( $added_classes ) {
1017
			$classes[] = esc_attr( $added_classes );
1018
		}
1019
1020
		/**
1021
		 * Globally filter row classes
1022
		 *
1023
		 * @since 2.0.0
1024
		 *
1025
		 * @param string            $classes Space-separated list of row classes
1026
		 * @param CMB2_Field object $field   This field object
1027
		 */
1028
		return apply_filters( 'cmb2_row_classes', implode( ' ', $classes ), $this );
1029
	}
1030
1031
	/**
1032
	 * Get field display callback and render the display value in the column.
1033
	 *
1034
	 * @since 2.2.2
1035
	 */
1036
	public function render_column() {
1037
		$this->render_context = 'display';
1038
1039
		$this->peform_param_callback( 'display_cb' );
1040
1041
		// For chaining.
1042
		return $this;
1043
	}
1044
1045
	/**
1046
	 * The method to fetch the value for this field for the REST API.
1047
	 *
1048
	 * @since 2.5.0
1049
	 */
1050
	public function get_rest_value() {
1051
		$field_type = $this->type();
1052
		$field_id   = $this->id( true );
1053
1054
		if ( $cb = $this->maybe_callback( 'rest_value_cb' ) ) {
1055
			add_filter( "cmb2_get_rest_value_for_{$field_id}", $cb, 99 );
1056
		}
1057
1058
		$value = $this->get_data();
1059
1060
		/**
1061
		 * Filter the value before it is sent to the REST request.
1062
		 *
1063
		 * @since 2.5.0
1064
		 *
1065
		 * @param mixed      $value The value from CMB2_Field::get_data()
1066
		 * @param CMB2_Field $field This field object.
1067
		 */
1068
		$value = apply_filters( 'cmb2_get_rest_value', $value, $this );
1069
1070
		/**
1071
		 * Filter the value before it is sent to the REST request.
1072
		 *
1073
		 * The dynamic portion of the hook name, $field_type, refers to the field type.
1074
		 *
1075
		 * @since 2.5.0
1076
		 *
1077
		 * @param mixed      $value The value from CMB2_Field::get_data()
1078
		 * @param CMB2_Field $field This field object.
1079
		 */
1080
		$value = apply_filters( "cmb2_get_rest_value_{$field_type}", $value, $this );
1081
1082
		/**
1083
		 * Filter the value before it is sent to the REST request.
1084
		 *
1085
		 * The dynamic portion of the hook name, $field_id, refers to the field id.
1086
		 *
1087
		 * @since 2.5.0
1088
		 *
1089
		 * @param mixed      $value The value from CMB2_Field::get_data()
1090
		 * @param CMB2_Field $field This field object.
1091
		 */
1092
		return apply_filters( "cmb2_get_rest_value_for_{$field_id}", $value, $this );
1093
	}
1094
1095
	/**
1096
	 * Get a field object for a supporting field. (e.g. file field)
1097
	 *
1098
	 * @since  2.7.0
1099
	 *
1100
	 * @return CMB2_Field|bool Supporting field object, if supported.
1101
	 */
1102
	public function get_supporting_field() {
1103
		$suffix = $this->args( 'has_supporting_data' );
1104
		if ( empty( $suffix ) ) {
1105
			return false;
1106
		}
1107
1108
		return $this->get_field_clone( array(
1109
			'id' => $this->_id( '', false ) . $suffix,
1110
			'sanitization_cb' => false,
1111
		) );
1112
	}
1113
1114
	/**
1115
	 * Default callback to outputs field value in a display format.
1116
	 *
1117
	 * @since 2.2.2
1118
	 */
1119
	public function display_value_callback() {
1120
		// If field is requesting to be conditionally shown.
1121
		if ( ! $this->should_show() ) {
1122
			return;
1123
		}
1124
1125
		$display = new CMB2_Field_Display( $this );
1126
		$field_type = $this->type();
1127
1128
		/**
1129
		 * A filter to bypass the default display.
1130
		 *
1131
		 * The dynamic portion of the hook name, $field_type, refers to the field type.
1132
		 *
1133
		 * Passing a non-null value to the filter will short-circuit the default display.
1134
		 *
1135
		 * @param bool|mixed         $pre_output Default null value.
1136
		 * @param CMB2_Field         $field      This field object.
1137
		 * @param CMB2_Field_Display $display    The `CMB2_Field_Display` object.
1138
		 */
1139
		$pre_output = apply_filters( "cmb2_pre_field_display_{$field_type}", null, $this, $display );
1140
1141
		if ( null !== $pre_output ) {
1142
			echo $pre_output;
1143
			return;
1144
		}
1145
1146
		$this->peform_param_callback( 'before_display_wrap' );
1147
1148
		printf( "<div class=\"cmb-column %s\" data-fieldtype=\"%s\">\n", $this->row_classes(), $field_type );
1149
1150
		$this->peform_param_callback( 'before_display' );
1151
1152
		CMB2_Field_Display::get( $this )->display();
1153
1154
		$this->peform_param_callback( 'after_display' );
1155
1156
		echo "\n</div>";
1157
1158
		$this->peform_param_callback( 'after_display_wrap' );
1159
1160
		// For chaining.
1161
		return $this;
1162
	}
1163
1164
	/**
1165
	 * Replaces a hash key - {#} - with the repeatable index
1166
	 *
1167
	 * @since  1.2.0
1168
	 * @param  string $value Value to update.
1169
	 * @return string        Updated value
1170
	 */
1171
	public function replace_hash( $value ) {
1172
		// Replace hash with 1 based count.
1173
		return str_replace( '{#}', ( $this->index + 1 ), $value );
1174
	}
1175
1176
	/**
1177
	 * Retrieve text parameter from field's text array (if it has one), or use fallback text
1178
	 * For back-compatibility, falls back to checking the options array.
1179
	 *
1180
	 * @since  2.2.2
1181
	 * @param  string $text_key Key in field's text array.
1182
	 * @param  string $fallback Fallback text.
1183
	 * @return string            Text
1184
	 */
1185
	public function get_string( $text_key, $fallback ) {
1186
		// If null, populate with our field strings values.
1187
		if ( null === $this->strings ) {
1188
			$this->strings = (array) $this->args['text'];
1189
1190
			if ( is_callable( $this->args['text_cb'] ) ) {
1191
				$strings = call_user_func( $this->args['text_cb'], $this );
1192
1193
				if ( $strings && is_array( $strings ) ) {
1194
					$this->strings += $strings;
1195
				}
1196
			}
1197
		}
1198
1199
		// If we have that string value, send it back.
1200
		if ( isset( $this->strings[ $text_key ] ) ) {
1201
			return $this->strings[ $text_key ];
1202
		}
1203
1204
		// Check options for back-compat.
1205
		$string = $this->options( $text_key );
1206
1207
		return $string ? $string : $fallback;
1208
	}
1209
1210
	/**
1211
	 * Retrieve options args.
1212
	 *
1213
	 * @since  2.0.0
1214
	 * @param  string $key Specific option to retrieve.
1215
	 * @return array|mixed Array of options or specific option.
1216
	 */
1217
	public function options( $key = '' ) {
1218
		if ( empty( $this->field_options ) ) {
1219
			$this->set_options();
1220
		}
1221
1222
		if ( $key ) {
1223
			return array_key_exists( $key, $this->field_options ) ? $this->field_options[ $key ] : false;
1224
		}
1225
1226
		return $this->field_options;
1227
	}
1228
1229
	/**
1230
	 * Generates/sets options args. Calls options_cb if it exists.
1231
	 *
1232
	 * @since  2.2.5
1233
	 *
1234
	 * @return array Array of options
1235
	 */
1236
	public function set_options() {
1237
		$this->field_options = (array) $this->args['options'];
1238
1239
		if ( is_callable( $this->args['options_cb'] ) ) {
1240
			$options = call_user_func( $this->args['options_cb'], $this );
1241
1242
			if ( $options && is_array( $options ) ) {
1243
				$this->field_options = $options + $this->field_options;
1244
			}
1245
		}
1246
1247
		return $this->field_options;
1248
	}
1249
1250
	/**
1251
	 * Store JS dependencies as part of the field args.
1252
	 *
1253
	 * @since 2.2.0
1254
	 * @param array $dependencies Dependies to register for this field.
1255
	 */
1256
	public function add_js_dependencies( $dependencies = array() ) {
1257
		foreach ( (array) $dependencies as $dependency ) {
1258
			$this->args['js_dependencies'][ $dependency ] = $dependency;
1259
		}
1260
1261
		CMB2_JS::add_dependencies( $dependencies );
1262
	}
1263
1264
	/**
1265
	 * Send field data to JS.
1266
	 *
1267
	 * @since 2.2.0
1268
	 */
1269
	public function register_js_data() {
1270
		if ( $this->group ) {
1271
			CMB2_JS::add_field_data( $this->group );
1272
		}
1273
1274
		return CMB2_JS::add_field_data( $this );
1275
	}
1276
1277
	/**
1278
	 * Get an array of some of the field data to be used in the Javascript.
1279
	 *
1280
	 * @since  2.2.4
1281
	 *
1282
	 * @return array
1283
	 */
1284
	public function js_data() {
1285
		return array(
1286
			'label'     => $this->args( 'name' ),
1287
			'id'        => $this->id( true ),
1288
			'type'      => $this->type(),
1289
			'hash'      => $this->hash_id(),
1290
			'box'       => $this->cmb_id,
1291
			'id_attr'   => $this->id(),
1292
			'name_attr' => $this->args( '_name' ),
1293
			'default'   => $this->get_default(),
1294
			'group'     => $this->group_id(),
1295
			'index'     => $this->group ? $this->group->index : null,
1296
		);
1297
	}
1298
1299
	/**
1300
	 * Returns a unique hash representing this field.
1301
	 *
1302
	 * @since  2.2.4
1303
	 *
1304
	 * @return string
1305
	 */
1306
	public function hash_id() {
1307
		if ( '' === $this->hash_id ) {
1308
			$this->hash_id = CMB2_Utils::generate_hash( $this->cmb_id . '||' . $this->id() );
1309
		}
1310
1311
		return $this->hash_id;
1312
	}
1313
1314
	/**
1315
	 * Gets the id of the group field if this field is part of a group.
1316
	 *
1317
	 * @since  2.2.4
1318
	 *
1319
	 * @return string
1320
	 */
1321
	public function group_id() {
1322
		return $this->group ? $this->group->id( true ) : '';
1323
	}
1324
1325
	/**
1326
	 * Get CMB2_Field default value, either from default param or default_cb param.
1327
	 *
1328
	 * @since  0.2.2
1329
	 *
1330
	 * @return mixed  Default field value
1331
	 */
1332
	public function get_default() {
1333
		$default = $this->args['default'];
1334
1335
		if ( null !== $default ) {
1336
			return apply_filters( 'cmb2_default_filter', $default, $this );
1337
		}
1338
1339
		$param = is_callable( $this->args['default_cb'] ) ? 'default_cb' : 'default';
1340
		$default = $this->args['default'] = $this->get_param_callback_result( $param );
1341
1342
		// Allow a filter override of the default value.
1343
		return apply_filters( 'cmb2_default_filter', $this->args['default'], $this );
1344
	}
1345
1346
	/**
1347
	 * Fills in empty field parameters with defaults
1348
	 *
1349
	 * @since 1.1.0
1350
	 *
1351
	 * @param  array $args Field config array.
1352
	 * @return array        Modified field config array.
1353
	 */
1354
	public function _set_field_defaults( $args ) {
1355
		$defaults = $this->get_default_field_args( $args );
1356
1357
		/**
1358
		 * Filter the CMB2 Field defaults.
1359
		 *
1360
		 * @since 2.6.0
1361
		 * @param array             $defaults Metabox field config array defaults.
1362
		 * @param string            $id       Field id for the current field to allow for selective filtering.
1363
		 * @param string            $type     Field type for the current field to allow for selective filtering.
1364
		 * @param CMB2_Field object $field    This field object.
1365
		 */
1366
		$defaults = apply_filters( 'cmb2_field_defaults', $defaults, $args['id'], $args['type'], $this );
1367
1368
		// Set up blank or default values for empty ones.
1369
		$args = wp_parse_args( $args, $defaults );
1370
1371
		/**
1372
		 * Filtering the CMB2 Field arguments once merged with the defaults, but before further processing.
1373
		 *
1374
		 * @since 2.6.0
1375
		 * @param array             $args  Metabox field config array defaults.
1376
		 * @param CMB2_Field object $field This field object.
1377
		 */
1378
		$args = apply_filters( 'cmb2_field_arguments_raw', $args, $this );
1379
1380
		/*
1381
		 * Deprecated usage:
1382
		 *
1383
		 * 'std' -- use 'default' (no longer works)
1384
		 * 'row_classes' -- use 'class', or 'class_cb'
1385
		 * 'default' -- as callback (use default_cb)
1386
		 */
1387
		$args = $this->convert_deprecated_params( $args );
1388
1389
		$args['repeatable'] = $args['repeatable'] && ! $this->repeatable_exception( $args['type'] );
1390
		$args['inline']     = $args['inline'] || false !== stripos( $args['type'], '_inline' );
1391
		$args['_id']        = $args['id'];
1392
		$args['_name']      = $args['id'];
1393
1394
		if ( $method = $this->has_args_method( $args['type'] ) ) {
1395
			$args = $this->{$method}( $args );
1396
		}
1397
1398
		if ( $this->group ) {
1399
			$args = $this->set_group_sub_field_defaults( $args );
1400
		}
1401
1402
		$with_supporting = array(
1403
			// CMB2_Sanitize::_save_file_id_value()/CMB2_Sanitize::_get_group_file_value_array().
1404
			'file' => '_id',
1405
			// See CMB2_Sanitize::_save_utc_value().
1406
			'text_datetime_timestamp_timezone' => '_utc',
1407
		);
1408
1409
		$args['has_supporting_data'] = isset( $with_supporting[ $args['type'] ] )
1410
			? $with_supporting[ $args['type'] ]
1411
			: false;
1412
1413
		// Repeatable fields require jQuery sortable library.
1414
		if ( ! empty( $args['repeatable'] ) ) {
1415
			CMB2_JS::add_dependencies( 'jquery-ui-sortable' );
1416
		}
1417
1418
		/**
1419
		 * Filter the CMB2 Field arguments after processing.
1420
		 *
1421
		 * @since 2.6.0
1422
		 * @param array             $args  Metabox field config array after processing.
1423
		 * @param CMB2_Field object $field This field object.
1424
		 */
1425
		return apply_filters( 'cmb2_field_arguments', $args, $this );
1426
	}
1427
1428
	/**
1429
	 * Sets default arguments for the group field types.
1430
	 *
1431
	 * @since 2.2.6
1432
	 *
1433
	 * @param  array $args Field config array.
1434
	 * @return array        Modified field config array.
1435
	 */
1436
	protected function set_field_defaults_group( $args ) {
1437
		$args['options'] = wp_parse_args( $args['options'], array(
1438
			'add_button'     => esc_html__( 'Add Group', 'cmb2' ),
1439
			'remove_button'  => esc_html__( 'Remove Group', 'cmb2' ),
1440
			'remove_confirm' => '',
1441
		) );
1442
1443
		return $args;
1444
	}
1445
1446
	/**
1447
	 * Sets default arguments for the wysiwyg field types.
1448
	 *
1449
	 * @since 2.2.6
1450
	 *
1451
	 * @param  array $args Field config array.
1452
	 * @return array        Modified field config array.
1453
	 */
1454
	protected function set_field_defaults_wysiwyg( $args ) {
1455
		$args['id'] = strtolower( str_ireplace( '-', '_', $args['id'] ) );
1456
		$args['options']['textarea_name'] = $args['_name'];
1457
1458
		return $args;
1459
	}
1460
1461
	/**
1462
	 * Sets default arguments for the all-or-nothing field types.
1463
	 *
1464
	 * @since 2.2.6
1465
	 *
1466
	 * @param  array $args Field config array.
1467
	 * @return array        Modified field config array.
1468
	 */
1469
	protected function set_field_defaults_all_or_nothing_types( $args ) {
1470
		$args['show_option_none'] = isset( $args['show_option_none'] ) ? $args['show_option_none'] : null;
1471
		$args['show_option_none'] = true === $args['show_option_none'] ? esc_html__( 'None', 'cmb2' ) : $args['show_option_none'];
1472
1473
		if ( null === $args['show_option_none'] ) {
1474
			$off_by_default = in_array( $args['type'], array( 'select', 'radio', 'radio_inline' ), true );
1475
			$args['show_option_none'] = $off_by_default ? false : esc_html__( 'None', 'cmb2' );
1476
		}
1477
1478
		return $args;
1479
	}
1480
1481
	/**
1482
	 * Sets default arguments for group sub-fields.
1483
	 *
1484
	 * @since 2.2.6
1485
	 *
1486
	 * @param  array $args Field config array.
1487
	 * @return array        Modified field config array.
1488
	 */
1489
	protected function set_group_sub_field_defaults( $args ) {
1490
		$args['id']    = $this->group->args( 'id' ) . '_' . $this->group->index . '_' . $args['id'];
1491
		$args['_name'] = $this->group->args( 'id' ) . '[' . $this->group->index . '][' . $args['_name'] . ']';
1492
1493
		return $args;
1494
	}
1495
1496
	/**
1497
	 * Gets the default arguments for all fields.
1498
	 *
1499
	 * @since 2.2.6
1500
	 *
1501
	 * @param  array $args Field config array.
1502
	 * @return array        Field defaults.
1503
	 */
1504
	protected function get_default_field_args( $args ) {
1505
		$type = isset( $args['type'] ) ? $args['type'] : '';
1506
1507
		return array(
1508
			'type'              => $type,
1509
			'name'              => '',
1510
			'desc'              => '',
1511
			'before'            => '',
1512
			'after'             => '',
1513
			'options'           => array(),
1514
			'options_cb'        => '',
1515
			'text'              => array(),
1516
			'text_cb'           => '',
1517
			'attributes'        => array(),
1518
			'protocols'         => null,
1519
			'default'           => null,
1520
			'default_cb'        => '',
1521
			'classes'           => null,
1522
			'classes_cb'        => '',
1523
			'select_all_button' => true,
1524
			'multiple'          => false,
1525
			'repeatable'        => 'group' === $type,
1526
			'inline'            => false,
1527
			'on_front'          => true,
1528
			'show_names'        => true,
1529
			'save_field'        => true, // Will not save if false.
1530
			'date_format'       => 'm\/d\/Y',
1531
			'time_format'       => 'h:i A',
1532
			'description'       => isset( $args['desc'] ) ? $args['desc'] : '',
1533
			'preview_size'      => 'file' === $type ? array( 350, 350 ) : array( 50, 50 ),
1534
			'render_row_cb'     => array( $this, 'render_field_callback' ),
1535
			'display_cb'        => array( $this, 'display_value_callback' ),
1536
			'label_cb'          => 'title' !== $type ? array( $this, 'label' ) : '',
1537
			'column'            => false,
1538
			'js_dependencies'   => array(),
1539
			'show_in_rest'      => null,
1540
			'char_counter'      => false,
1541
			'char_max'          => false,
1542
			'char_max_enforce'  => false,
1543
		);
1544
	}
1545
1546
	/**
1547
	 * Get default field arguments specific to this CMB2 object.
1548
	 *
1549
	 * @since  2.2.0
1550
	 * @param  array      $field_args  Metabox field config array.
1551
	 * @param  CMB2_Field $field_group (optional) CMB2_Field object (group parent).
1552
	 * @return array                   Array of field arguments.
1553
	 */
1554
	protected function get_default_args( $field_args, $field_group = null ) {
1555
		$args = parent::get_default_args( array(), $this->group );
1556
1557
		if ( isset( $field_args['field_args'] ) ) {
1558
			$args = wp_parse_args( $field_args, $args );
1559
		} else {
1560
			$args['field_args'] = wp_parse_args( $field_args, $this->args );
1561
		}
1562
1563
		return $args;
1564
	}
1565
1566
	/**
1567
	 * Returns a cloned version of this field object, but with
1568
	 * modified/overridden field arguments.
1569
	 *
1570
	 * @since  2.2.2
1571
	 * @param  array $field_args Array of field arguments, or entire array of
1572
	 *                           arguments for CMB2_Field.
1573
	 *
1574
	 * @return CMB2_Field         The new CMB2_Field instance.
1575
	 */
1576
	public function get_field_clone( $field_args ) {
1577
		return $this->get_new_field( $field_args );
1578
	}
1579
1580
	/**
1581
	 * Returns the CMB2 instance this field is registered to.
1582
	 *
1583
	 * @since  2.2.2
1584
	 *
1585
	 * @return CMB2|WP_Error If new CMB2_Field is called without cmb_id arg, returns error.
1586
	 */
1587
	public function get_cmb() {
1588
		if ( ! $this->cmb_id ) {
1589
			return new WP_Error( 'no_cmb_id', esc_html__( 'Sorry, this field does not have a cmb_id specified.', 'cmb2' ) );
1590
		}
1591
1592
		return cmb2_get_metabox( $this->cmb_id, $this->object_id, $this->object_type );
1593
	}
1594
1595
	/**
1596
	 * Converts deprecated field parameters to the current/proper parameter, and throws a deprecation notice.
1597
	 *
1598
	 * @since  2.2.3
1599
	 * @param  array $args Metabox field config array.
1600
	 * @return array       Modified field config array.
1601
	 */
1602
	protected function convert_deprecated_params( $args ) {
1603
1604
		if ( isset( $args['row_classes'] ) ) {
1605
1606
			// We'll let this one be.
1607
			// $this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_PARAM, 'row_classes', 'classes' );
1608
			// row_classes param could be a callback. This is definitely deprecated.
1609
			if ( is_callable( $args['row_classes'] ) ) {
1610
1611
				$this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'row_classes', 'classes_cb' );
1612
1613
				$args['classes_cb'] = $args['row_classes'];
1614
				$args['classes'] = null;
1615
			} else {
1616
1617
				$args['classes'] = $args['row_classes'];
1618
			}
1619
1620
			unset( $args['row_classes'] );
1621
		}
1622
1623
		// default param can be passed a callback as well.
1624
		if ( is_callable( $args['default'] ) ) {
1625
1626
			$this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'default', 'default_cb' );
1627
1628
			$args['default_cb'] = $args['default'];
1629
			$args['default'] = null;
1630
		}
1631
1632
		// options param can be passed a callback as well.
1633
		if ( is_callable( $args['options'] ) ) {
1634
1635
			$this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'options', 'options_cb' );
1636
1637
			$args['options_cb'] = $args['options'];
1638
			$args['options'] = array();
1639
		}
1640
1641
		return $args;
1642
	}
1643
1644
}
1645