Completed
Pull Request — trunk (#541)
by Justin
06:40
created

CMB2_Field   D

Complexity

Total Complexity 163

Size/Duplication

Total Lines 1299
Duplicated Lines 1.92 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 88.29%

Importance

Changes 17
Bugs 4 Features 3
Metric Value
c 17
b 4
f 3
dl 25
loc 1299
ccs 392
cts 444
cp 0.8829
rs 4.4102
wmc 163
lcom 1
cbo 6

40 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 22 7
A __call() 0 4 2
A id() 0 4 2
A args() 0 14 4
A _data() 0 7 3
A value() 0 3 1
C get_data() 0 63 9
C update_data() 0 64 8
A remove_data() 0 56 4
A data_args() 0 10 1
B sanitization_cb() 0 43 6
A save_field_from_data() 0 9 2
C save_field() 0 66 12
B maybe_callback() 0 18 6
A escaping_exception() 0 8 1
B repeatable_exception() 0 30 1
D escaped_value() 0 40 10
A val_or_default() 0 3 2
A field_timezone_offset() 0 3 1
A field_timezone() 0 14 3
A format_timestamp() 0 3 1
A get_timestamp_format() 0 12 4
A get_timestamp_from_value() 0 3 1
A render_field() 0 8 1
B render_field_callback() 0 44 6
A label() 0 9 3
C row_classes() 0 48 7
A render_column() 0 8 1
B display_value_callback() 0 43 3
A should_show() 11 11 2
A peform_param_callback() 0 3 1
B get_param_callback_result() 0 31 5
A replace_hash() 0 4 1
C string() 7 24 7
D options() 7 25 9
A add_js_dependencies() 0 7 2
A get_default() 0 13 3
F _set_field_defaults() 0 96 17
A get_field_clone() 0 17 2
A get_cmb() 0 7 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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    WebDevStudios
10
 * @license   GPL-2.0+
11
 * @link      http://webdevstudios.com
12
 *
13
 * @method string _id()
14
 * @method string type()
15
 * @method mixed fields()
16
 */
17
class CMB2_Field {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
18
19
	/**
20
	 * Metabox object id
21
	 * @var   mixed
22
	 * @since 1.1.0
23
	 */
24
	public $object_id = null;
25
26
	/**
27
	 * Metabox object type
28
	 * @var   string
29
	 * @since 1.1.0
30
	 */
31
	public $object_type = '';
32
33
	/**
34
	 * Field arguments
35
	 * @var   mixed
36
	 * @since 1.1.0
37
	 */
38
	public $args = array();
39
40
	/**
41
	 * Field group object or false (if no group)
42
	 * @var   mixed
43
	 * @since 1.1.0
44
	 */
45
	public $group = false;
46
47
	/**
48
	 * Field meta value
49
	 * @var   mixed
50
	 * @since 1.1.0
51
	 */
52
	public $value = null;
53
54
	/**
55
	 * Field meta value
56
	 * @var   mixed
57
	 * @since 1.1.0
58
	 */
59
	public $escaped_value = null;
60
61
	/**
62
	 * Grouped Field's current numeric index during the save process
63
	 * @var   mixed
64
	 * @since 2.0.0
65
	 */
66
	public $index = 0;
67
68
	/**
69
	 * Array of field options
70
	 * @var   array
71
	 * @since 2.0.0
72
	 */
73
	protected $field_options = array();
74
75
	/**
76
	 * Array of provided field text strings
77
	 * @var   array
78
	 * @since 2.0.0
79
	 */
80
	protected $strings;
81
82
	/**
83
	 * Array of field param callback results
84
	 * @var   array
85
	 * @since 2.0.0
86
	 */
87
	protected $callback_results = array();
88
89
	/**
90
	 * Array of key => value data for saving. Likely $_POST data.
91
	 * @var   array
92
	 * @since 2.0.0
93
	 */
94
	public $data_to_save = array();
95
96
	/**
97
	 * Current field's CMB2 instance ID
98
	 * @var   string
99
	 * @since 2.2.2
100
	 */
101
	public $cmb_id = '';
102
103
	/**
104
	 * The field's render context. In most cases, 'edit', but can be 'display'.
105
	 * @var   string
106
	 * @since 2.2.2
107
	 */
108
	public $render_context = 'edit';
109
110
	/**
111
	 * All CMB2_Field callable field arguments.
112
	 * Can be used to determine if a field argument is callable.
113
	 *
114
	 * @var array
115
	 */
116
	public static $callable_fields = array(
117
		'default',
118
		'row_classes',
119
		'options_cb',
120
		'label_cb',
121
		'render_row_cb',
122
		'before_group',
123
		'before_group_row',
124
		'before_row',
125
		'before',
126
		'before_field',
127
		'after_field',
128
		'after',
129
		'after_row',
130
		'after_group_row',
131
		'after_group',
132
	);
133
134
	/**
135
	 * Constructs our field object
136
	 * @since 1.1.0
137
	 * @param array $args Field arguments
138
	 */
139 107
	public function __construct( $args ) {
140
141 107
		if ( ! empty( $args['group_field'] ) ) {
142 3
			$this->group       = $args['group_field'];
143 3
			$this->object_id   = $this->group->object_id;
144 3
			$this->object_type = $this->group->object_type;
145 3
			$this->cmb_id      = $this->group->cmb_id;
146 3
		} else {
147 107
			$this->object_id   = isset( $args['object_id'] ) && '_' !== $args['object_id'] ? $args['object_id'] : 0;
148 107
			$this->object_type = isset( $args['object_type'] ) ? $args['object_type'] : 'post';
149
150 107
			if ( isset( $args['cmb_id'] ) ) {
151 38
				$this->cmb_id = $args['cmb_id'];
152 38
			}
153
		}
154
155 107
		$this->args = $this->_set_field_defaults( $args['field_args'] );
156
157 107
		if ( $this->object_id ) {
158 101
			$this->value = $this->get_data();
159 101
		}
160 107
	}
161
162
	/**
163
	 * Non-existent methods fallback to checking for field arguments of the same name
164
	 * @since  1.1.0
165
	 * @param  string $name     Method name
166
	 * @param  array  $arguments Array of passed-in arguments
167
	 * @return mixed             Value of field argument
168
	 */
169 95
	public function __call( $name, $arguments ) {
170 95
		$key = isset( $arguments[0] ) ? $arguments[0] : false;
171 95
		return $this->args( $name, $key );
172
	}
173
174
	/**
175
	 * Retrieves the field id
176
	 * @since  1.1.0
177
	 * @param  boolean $raw Whether to retrieve pre-modidifed id
178
	 * @return string       Field id
179
	 */
180 105
	public function id( $raw = false ) {
181 105
		$id = $raw ? '_id' : 'id';
182 105
		return $this->args( $id );
183
	}
184
185
	/**
186
	 * Get a field argument
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 108
	public function args( $key = '', $_key = '' ) {
193 108
		$arg = $this->_data( 'args', $key );
194
195 108
		if ( in_array( $key, array( 'default', 'default_cb' ), true ) ) {
196
197 1
			$arg = $this->get_default();
198
199 108
		} elseif ( $_key ) {
200
201
			$arg = isset( $arg[ $_key ] ) ? $arg[ $_key ] : false;
202
		}
203
204 108
		return $arg;
205
	}
206
207
	/**
208
	 * Retrieve a portion of a field property
209
	 * @since  1.1.0
210
	 * @param  string  $var Field property to check
211
	 * @param  string  $key Field property array key to check
212
	 * @return mixed        Queried property value or false
213
	 */
214 108
	public function _data( $var, $key = '' ) {
215 108
		$vars = $this->$var;
216 108
		if ( $key ) {
217 108
			return array_key_exists( $key, $vars ) ? $vars[ $key ] : false;
218
		}
219 96
		return $vars;
220
	}
221
222
	/**
223
	 * Get Field's value
224
	 * @since  1.1.0
225
	 * @param  string $key If value is an array, is used to get array key->value
226
	 * @return mixed       Field value or false if non-existent
227
	 */
228 46
	public function value( $key = '' ) {
229 46
		return $this->_data( 'value', $key );
230
	}
231
232
	/**
233
	 * Retrieves metadata/option data
234
	 * @since  1.0.1
235
	 * @param  string $field_id Meta key/Option array key
236
	 * @param  array  $args     Override arguments
237
	 * @return mixed            Meta/Option value
238
	 */
239 103
	public function get_data( $field_id = '', $args = array() ) {
240 103
		if ( $field_id ) {
241
			$args['field_id'] = $field_id;
242 103
		} else if ( $this->group ) {
243
			$args['field_id'] = $this->group->id();
244
		}
245
246 103
		$a = $this->data_args( $args );
247
248
		/**
249
		 * Filter whether to override getting of meta value.
250
		 * Returning a non 'cmb2_field_no_override_val' value
251
		 * will effectively short-circuit the value retrieval.
252
		 *
253
		 * @since 2.0.0
254
		 *
255
		 * @param mixed $value     The value get_metadata() should
256
		 *                         return - a single metadata value,
257
		 *                         or an array of values.
258
		 *
259
		 * @param int   $object_id Object ID.
260
		 *
261
		 * @param array $args {
262
		 *     An array of arguments for retrieving data
263
		 *
264
		 *     @type string $type     The current object type
265
		 *     @type int    $id       The current object ID
266
		 *     @type string $field_id The ID of the field being requested
267
		 *     @type bool   $repeat   Whether current field is repeatable
268
		 *     @type bool   $single   Whether current field is a single database row
269
		 * }
270
		 *
271
		 * @param CMB2_Field object $field This field object
272
		 */
273 103
		$data = apply_filters( 'cmb2_override_meta_value', 'cmb2_field_no_override_val', $this->object_id, $a, $this );
274
275
		/**
276
		 * Filter and parameters are documented for 'cmb2_override_meta_value' filter (above).
277
		 *
278
		 * The dynamic portion of the hook, $field_id, refers to the current
279
		 * field id paramater. Returning a non 'cmb2_field_no_override_val' value
280
		 * will effectively short-circuit the value retrieval.
281
		 *
282
		 * @since 2.0.0
283
		 */
284 103
		$data = apply_filters( "cmb2_override_{$a['field_id']}_meta_value", $data, $this->object_id, $a, $this );
285
286
		// If no override, get value normally
287 103
		if ( 'cmb2_field_no_override_val' === $data ) {
288 102
			$data = 'options-page' === $a['type']
289 102
				? cmb2_options( $a['id'] )->get( $a['field_id'] )
290 102
				: get_metadata( $a['type'], $a['id'], $a['field_id'], ( $a['single'] || $a['repeat'] ) );
291 102
		}
292
293 103
		if ( $this->group ) {
294
295
			$data = is_array( $data ) && isset( $data[ $this->group->index ][ $this->args( '_id' ) ] )
296
				? $data[ $this->group->index ][ $this->args( '_id' ) ]
297
				: false;
298
		}
299
300 103
		return $data;
301
	}
302
303
	/**
304
	 * Updates metadata/option data
305
	 * @since  1.0.1
306
	 * @param  mixed $new_value Value to update data with
307
	 * @param  bool  $single    Whether data is an array (add_metadata)
308
	 */
309 10
	public function update_data( $new_value, $single = true ) {
310 10
		$a = $this->data_args( array( 'single' => $single ) );
311
312 10
		$a['value'] = $a['repeat'] ? array_values( $new_value ) : $new_value;
313
314
		/**
315
		 * Filter whether to override saving of meta value.
316
		 * Returning a non-null value will effectively short-circuit the function.
317
		 *
318
		 * @since 2.0.0
319
		 *
320
		 * @param null|bool $check  Whether to allow updating metadata for the given type.
321
		 *
322
		 * @param array $args {
323
		 *     Array of data about current field including:
324
		 *
325
		 *     @type string $value    The value to set
326
		 *     @type string $type     The current object type
327
		 *     @type int    $id       The current object ID
328
		 *     @type string $field_id The ID of the field being updated
329
		 *     @type bool   $repeat   Whether current field is repeatable
330
		 *     @type bool   $single   Whether current field is a single database row
331
		 * }
332
		 *
333
		 * @param array $field_args All field arguments
334
		 *
335
		 * @param CMB2_Field object $field This field object
336
		 */
337 10
		$override = apply_filters( 'cmb2_override_meta_save', null, $a, $this->args(), $this );
338
339
		/**
340
		 * Filter and parameters are documented for 'cmb2_override_meta_save' filter (above).
341
		 *
342
		 * The dynamic portion of the hook, $a['field_id'], refers to the current
343
		 * field id paramater. Returning a non-null value
344
		 * will effectively short-circuit the function.
345
		 *
346
		 * @since 2.0.0
347
		 */
348 10
		$override = apply_filters( "cmb2_override_{$a['field_id']}_meta_save", $override, $a, $this->args(), $this );
349
350
		// If override, return that
351 10
		if ( null !== $override ) {
352 1
			return $override;
353
		}
354
355
		// Options page handling (or temp data store)
356 10
		if ( 'options-page' === $a['type'] || empty( $a['id'] ) ) {
357 3
			return cmb2_options( $a['id'] )->update( $a['field_id'], $a['value'], false, $a['single'] );
358
		}
359
360
		// Add metadata if not single
361 7
		if ( ! $a['single'] ) {
362 1
			return add_metadata( $a['type'], $a['id'], $a['field_id'], $a['value'], false );
363
		}
364
365
		// Delete meta if we have an empty array
366 6
		if ( is_array( $a['value'] ) && empty( $a['value'] ) ) {
367
			return delete_metadata( $a['type'], $a['id'], $a['field_id'], $this->value );
368
		}
369
370
		// Update metadata
371 6
		return update_metadata( $a['type'], $a['id'], $a['field_id'], $a['value'] );
372
	}
373
374
	/**
375
	 * Removes/updates metadata/option data
376
	 * @since  1.0.1
377
	 * @param  string $old Old value
378
	 */
379 3
	public function remove_data( $old = '' ) {
380 3
		$a = $this->data_args( array( 'old' => $old ) );
381
382
		/**
383
		 * Filter whether to override removing of meta value.
384
		 * Returning a non-null value will effectively short-circuit the function.
385
		 *
386
		 * @since 2.0.0
387
		 *
388
		 * @param null|bool $delete Whether to allow metadata deletion of the given type.
389
		 * @param array $args       Array of data about current field including:
390
		 *                              'type'     : Current object type
391
		 *                              'id'       : Current object ID
392
		 *                              'field_id' : Current Field ID
393
		 *                              'repeat'   : Whether current field is repeatable
394
		 *                              'single'   : Whether to save as a
395
		 *                              					single meta value
396
		 * @param array $field_args All field arguments
397
		 * @param CMB2_Field object $field This field object
398
		 */
399 3
		$override = apply_filters( 'cmb2_override_meta_remove', null, $a, $this->args(), $this );
400
401
		/**
402
		 * Filter whether to override removing of meta value.
403
		 *
404
		 * The dynamic portion of the hook, $a['field_id'], refers to the current
405
		 * field id paramater. Returning a non-null value
406
		 * will effectively short-circuit the function.
407
		 *
408
		 * @since 2.0.0
409
		 *
410
		 * @param null|bool $delete Whether to allow metadata deletion of the given type.
411
		 * @param array $args       Array of data about current field including:
412
		 *                              'type'     : Current object type
413
		 *                              'id'       : Current object ID
414
		 *                              'field_id' : Current Field ID
415
		 *                              'repeat'   : Whether current field is repeatable
416
		 *                              'single'   : Whether to save as a
417
		 *                              					single meta value
418
		 * @param array $field_args All field arguments
419
		 * @param CMB2_Field object $field This field object
420
		 */
421 3
		$override = apply_filters( "cmb2_override_{$a['field_id']}_meta_remove", $override, $a, $this->args(), $this );
422
423
		// If no override, remove as usual
424 3
		if ( null !== $override ) {
425
			return $override;
426
		}
427
		// Option page handling
428 3
		elseif ( 'options-page' === $a['type'] || empty( $a['id'] ) ) {
429 1
			return cmb2_options( $a['id'] )->remove( $a['field_id'] );
430
		}
431
432
		// Remove metadata
433 2
		return delete_metadata( $a['type'], $a['id'], $a['field_id'], $old );
434
	}
435
436
	/**
437
	 * Data variables for get/set data methods
438
	 * @since  1.1.0
439
	 * @param  array $args Override arguments
440
	 * @return array       Updated arguments
441
	 */
442 103
	public function data_args( $args = array() ) {
443 103
		$args = wp_parse_args( $args, array(
444 103
			'type'     => $this->object_type,
445 103
			'id'       => $this->object_id,
446 103
			'field_id' => $this->id( true ),
447 103
			'repeat'   => $this->args( 'repeatable' ),
448 103
			'single'   => ! $this->args( 'multiple' ),
449 103
		) );
450 103
		return $args;
451
	}
452
453
	/**
454
	 * Checks if field has a registered sanitization callback
455
	 * @since  1.0.1
456
	 * @param  mixed $meta_value Meta value
457
	 * @return mixed             Possibly sanitized meta value
458
	 */
459 11
	public function sanitization_cb( $meta_value ) {
460
461 11
		if ( $this->args( 'repeatable' ) && is_array( $meta_value ) ) {
462
			// Remove empties
463 1
			$meta_value = array_filter( $meta_value );
464 1
		}
465
466
		// Check if the field has a registered validation callback
467 11
		$cb = $this->maybe_callback( 'sanitization_cb' );
468 11
		if ( false === $cb ) {
469
			// If requesting NO validation, return meta value
470 1
			return $meta_value;
471 10
		} elseif ( $cb ) {
472
			// Ok, callback is good, let's run it.
473
			return call_user_func( $cb, $meta_value, $this->args(), $this );
474
		}
475
476 10
		$sanitizer = new CMB2_Sanitize( $this, $meta_value );
477
478
		/**
479
		 * Filter the value before it is saved.
480
		 *
481
		 * The dynamic portion of the hook name, $this->type(), refers to the field type.
482
		 *
483
		 * Passing a non-null value to the filter will short-circuit saving
484
		 * the field value, saving the passed value instead.
485
		 *
486
		 * @param bool|mixed $override_value Sanitization/Validation override value to return.
487
		 *                                   Default false to skip it.
488
		 * @param mixed      $value      The value to be saved to this field.
489
		 * @param int        $object_id  The ID of the object where the value will be saved
490
		 * @param array      $field_args The current field's arguments
491
		 * @param object     $sanitizer  This `CMB2_Sanitize` object
492
		 */
493 10
		$override_value = apply_filters( "cmb2_sanitize_{$this->type()}", null, $sanitizer->value, $this->object_id, $this->args(), $sanitizer );
494
495 10
		if ( null !== $override_value ) {
496 1
			return $override_value;
497
		}
498
499
		// Sanitization via 'CMB2_Sanitize'
500 9
		return $sanitizer->{$this->type()}();
501
	}
502
503
	/**
504
	 * Process $_POST data to save this field's value
505
	 * @since  2.0.3
506
	 * @param  array $data_to_save $_POST data to check
507
	 * @return bool                Result of save
508
	 */
509 2
	public function save_field_from_data( array $data_to_save ) {
510 2
		$this->data_to_save = $data_to_save;
511
512 2
		$meta_value = isset( $this->data_to_save[ $this->id( true ) ] )
513 2
			? $this->data_to_save[ $this->id( true ) ]
514 2
			: null;
515
516 2
		return $this->save_field( $meta_value );
517
	}
518
519
	/**
520
	 * Sanitize/store a value to this field
521
	 * @since  2.0.0
522
	 * @param  array $meta_value Desired value to sanitize/store
523
	 * @return bool              Result of save
524
	 */
525 10
	public function save_field( $meta_value ) {
526
527 10
		$new_value = $this->sanitization_cb( $meta_value );
528 10
		$old       = $this->get_data();
529 10
		$updated   = false;
530 10
		$action    = '';
531
532 10
		if ( $this->args( 'multiple' ) && ! $this->args( 'repeatable' ) && ! $this->group ) {
533
534 1
			$this->remove_data();
535 1
			$count = 0;
536
537 1
			if ( ! empty( $new_value ) ) {
538 1
				foreach ( $new_value as $add_new ) {
539 1
					if ( $this->update_data( $add_new, false ) ) {
540 1
						$count++;
541 1
					}
542 1
				}
543 1
			}
544
545 1
			$updated = $count ? $count : false;
546 1
			$action  = 'repeatable';
547
548 10
		} elseif ( ! cmb2_utils()->isempty( $new_value ) && $new_value !== $old ) {
549 8
			$updated = $this->update_data( $new_value );
550 8
			$action  = 'updated';
551 9
		} elseif ( cmb2_utils()->isempty( $new_value ) ) {
552 2
			$updated = $this->remove_data();
553 2
			$action  = 'removed';
554 2
		}
555
556 10
		if ( $updated ) {
557 10
			$this->value = $this->get_data();
558 10
			$this->escaped_value = null;
559 10
		}
560
561 10
		$field_id = $this->id( true );
562
563
		/**
564
		 * Hooks after save field action.
565
		 *
566
		 * @since 2.2.0
567
		 *
568
		 * @param string            $field_id the current field id paramater.
569
		 * @param bool              $updated  Whether the metadata update action occurred.
570
		 * @param string            $action   Action performed. Could be "repeatable", "updated", or "removed".
571
		 * @param CMB2_Field object $field    This field object
572
		 */
573 10
		do_action( 'cmb2_save_field', $field_id, $updated, $action, $this );
574
575
		/**
576
		 * Hooks after save field action.
577
		 *
578
		 * The dynamic portion of the hook, $field_id, refers to the
579
		 * current field id paramater.
580
		 *
581
		 * @since 2.2.0
582
		 *
583
		 * @param bool              $updated Whether the metadata update action occurred.
584
		 * @param string            $action  Action performed. Could be "repeatable", "updated", or "removed".
585
		 * @param CMB2_Field object $field   This field object
586
		 */
587 9
		do_action( "cmb2_save_field_{$field_id}", $updated, $action, $this );
588
589 8
		return $updated;
590
	}
591
592
	/**
593
	 * Checks if field has a callback value
594
	 * @since  1.0.1
595
	 * @param  string $cb Callback string
596
	 * @return mixed      NULL, false for NO validation, or $cb string if it exists.
597
	 */
598 96
	public function maybe_callback( $cb ) {
599 96
		$field_args = $this->args();
600 96
		if ( ! isset( $field_args[ $cb ] ) ) {
601 89
			return;
602
		}
603
604
		// Check if metabox is requesting NO validation
605 50
		$cb = false !== $field_args[ $cb ] && 'false' !== $field_args[ $cb ] ? $field_args[ $cb ] : false;
606
607
		// If requesting NO validation, return false
608 50
		if ( ! $cb ) {
609 9
			return false;
610
		}
611
612 50
		if ( is_callable( $cb ) ) {
613 49
			return $cb;
614
		}
615 5
	}
616
617
	/**
618
	 * Determine if current type is exempt from escaping
619
	 * @since  1.1.0
620
	 * @return bool  True if exempt
621
	 */
622 46
	public function escaping_exception() {
623
		// These types cannot be escaped
624 46
		return in_array( $this->type(), array(
625 46
			'file_list',
626 46
			'multicheck',
627 46
			'text_datetime_timestamp_timezone',
628 46
		) );
629
	}
630
631
	/**
632
	 * Determine if current type cannot be repeatable
633
	 * @since  1.1.0
634
	 * @param  string $type Field type to check
635
	 * @return bool         True if type cannot be repeatable
636
	 */
637 4
	public function repeatable_exception( $type ) {
638
		// These types cannot be escaped
639
		$internal_fields = array(
640
			// Use file_list instead
641 4
			'file'                => 1,
642 4
			'radio'               => 1,
643 4
			'title'               => 1,
644
			// @todo Ajax load wp_editor: http://wordpress.stackexchange.com/questions/51776/how-to-load-wp-editor-through-ajax-jquery
645 4
			'wysiwyg'             => 1,
646 4
			'checkbox'            => 1,
647 4
			'radio_inline'        => 1,
648 4
			'taxonomy_radio'      => 1,
649 4
			'taxonomy_select'     => 1,
650 4
			'taxonomy_multicheck' => 1,
651 4
		);
652
653
		/**
654
		 * Filter field types that are non-repeatable.
655
		 *
656
		 * Note that this does *not* allow overriding the default non-repeatable types.
657
		 *
658
		 * @since 2.1.1
659
		 *
660
		 * @param array $fields Array of fields designated as non-repeatable. Note that the field names are *keys*,
661
		 *                      and not values. The value can be anything, because it is meaningless. Example:
662
		 *                      array( 'my_custom_field' => 1 )
663
		 */
664 4
		$all_fields = array_merge( apply_filters( 'cmb2_non_repeatable_fields', array() ), $internal_fields );
665 4
		return isset( $all_fields[ $type ] );
666
	}
667
668
	/**
669
	 * Escape the value before output. Defaults to 'esc_attr()'
670
	 * @since  1.0.1
671
	 * @param  callable $func       Escaping function (if not esc_attr())
672
	 * @param  mixed    $meta_value Meta value
673
	 * @return mixed                Final value
674
	 */
675 46
	public function escaped_value( $func = 'esc_attr', $meta_value = '' ) {
676
677 46
		if ( null !== $this->escaped_value ) {
678 23
			return $this->escaped_value;
679
		}
680
681 46
		$meta_value = $meta_value ? $meta_value : $this->value();
682
683
		// Check if the field has a registered escaping callback
684 46
		if ( $cb = $this->maybe_callback( 'escape_cb' ) ) {
685
			// Ok, callback is good, let's run it.
686
			return call_user_func( $cb, $meta_value, $this->args(), $this );
687
		}
688
689
		// Or custom escaping filter can be used
690 46
		$esc = apply_filters( "cmb2_types_esc_{$this->type()}", null, $meta_value, $this->args(), $this );
691 46
		if ( null !== $esc ) {
692
			return $esc;
693
		}
694
695 46
		if ( false === $cb || $this->escaping_exception() ) {
696
			// If requesting NO escaping, return meta value
697 5
			return $this->val_or_default( $meta_value );
698
		}
699
700
		// escaping function passed in?
701 41
		$func       = $func ? $func : 'esc_attr';
702 41
		$meta_value = $this->val_or_default( $meta_value );
703
704 41
		if ( is_array( $meta_value ) ) {
705
			foreach ( $meta_value as $key => $value ) {
706
				$meta_value[ $key ] = call_user_func( $func, $value );
707
			}
708
		} else {
709 41
			$meta_value = call_user_func( $func, $meta_value );
710
		}
711
712 41
		$this->escaped_value = $meta_value;
713 41
		return $this->escaped_value;
714
	}
715
716
	/**
717
	 * Return non-empty value or field default if value IS empty
718
	 * @since  2.0.0
719
	 * @param  mixed $meta_value Field value
720
	 * @return mixed             Field value, or default value
721
	 */
722 46
	public function val_or_default( $meta_value ) {
723 46
		return ! cmb2_utils()->isempty( $meta_value ) ? $meta_value : $this->get_default();
724
	}
725
726
	/**
727
	 * Offset a time value based on timezone
728
	 * @since  1.0.0
729
	 * @return string Offset time string
730
	 */
731
	public function field_timezone_offset() {
732
		return cmb2_utils()->timezone_offset( $this->field_timezone() );
733
	}
734
735
	/**
736
	 * Return timezone string
737
	 * @since  1.0.0
738
	 * @return string Timezone string
739
	 */
740
	public function field_timezone() {
741
		$value = '';
742
743
		// Is timezone arg set?
744
		if ( $this->args( 'timezone' ) ) {
745
			$value = $this->args( 'timezone' );
746
		}
747
		// Is there another meta key with a timezone stored as its value we should use?
748
		else if ( $this->args( 'timezone_meta_key' ) ) {
749
			$value = $this->get_data( $this->args( 'timezone_meta_key' ) );
750
		}
751
752
		return $value;
753
	}
754
755
	/**
756
	 * Format the timestamp field value based on the field date/time format arg
757
	 * @since  2.0.0
758
	 * @param  int    $meta_value Timestamp
759
	 * @param  string $format     Either date_format or time_format
760
	 * @return string             Formatted date
761
	 */
762 10
	public function format_timestamp( $meta_value, $format = 'date_format' ) {
763 10
		return date( stripslashes( $this->args( $format ) ), $meta_value );
764
	}
765
766
	/**
767
	 * Return a formatted timestamp for a field
768
	 * @since  2.0.0
769
	 * @param  string $format     Either date_format or time_format
770
	 * @param  string $meta_value Optional meta value to check
771
	 * @return string             Formatted date
772
	 */
773 10
	public function get_timestamp_format( $format = 'date_format', $meta_value = 0 ) {
774 10
		$meta_value = $meta_value ? $meta_value : $this->escaped_value();
775 10
		$meta_value = cmb2_utils()->make_valid_time_stamp( $meta_value );
776
777 10
		if ( empty( $meta_value ) ) {
778
			return '';
779
		}
780
781 10
		return is_array( $meta_value )
782 10
			? array_map( array( $this, 'format_timestamp' ), $meta_value, $format )
783 10
			: $this->format_timestamp( $meta_value, $format );
784
	}
785
786
	/**
787
	 * Get timestamp from text date
788
	 * @since  2.2.0
789
	 * @param  string $value Date value
790
	 * @return mixed         Unix timestamp representing the date.
791
	 */
792
	public function get_timestamp_from_value( $value ) {
793
		return cmb2_utils()->get_timestamp_from_value( $value, $this->args( 'date_format' ) );
794
	}
795
796
	/**
797
	 * Get field render callback and Render the field row
798
	 * @since 1.0.0
799
	 */
800 10
	public function render_field() {
801 10
		$this->render_context = 'edit';
802
803 10
		$this->peform_param_callback( 'render_row_cb' );
804
805
		// For chaining
806 10
		return $this;
807
	}
808
809
	/**
810
	 * Default field render callback
811
	 * @since 2.1.1
812
	 */
813 9
	public function render_field_callback() {
814
815
		// If field is requesting to not be shown on the front-end
816 9
		if ( ! is_admin() && ! $this->args( 'on_front' ) ) {
817
			return;
818
		}
819
820
		// If field is requesting to be conditionally shown
821 9
		if ( ! $this->should_show() ) {
822
			return;
823
		}
824
825 9
		$this->peform_param_callback( 'before_row' );
826
827 9
		printf( "<div class=\"cmb-row %s\" data-fieldtype=\"%s\">\n", $this->row_classes(), $this->type() );
828
829 9
		if ( ! $this->args( 'show_names' ) ) {
830
			echo "\n\t<div class=\"cmb-td\">\n";
831
832
			$this->peform_param_callback( 'label_cb' );
833
834
		} else {
835
836 9
			if ( $this->get_param_callback_result( 'label_cb' ) ) {
837 9
				echo '<div class="cmb-th">', $this->peform_param_callback( 'label_cb' ), '</div>';
838 9
			}
839
840 9
			echo "\n\t<div class=\"cmb-td\">\n";
841
		}
842
843 9
		$this->peform_param_callback( 'before' );
844
845 9
		$field_type = new CMB2_Types( $this );
846 9
		$field_type->render();
847
848 9
		$this->peform_param_callback( 'after' );
849
850 9
		echo "\n\t</div>\n</div>";
851
852 9
		$this->peform_param_callback( 'after_row' );
853
854
		// For chaining
855 9
		return $this;
856
	}
857
858
	/**
859
	 * The default label_cb callback (if not a title field)
860
	 *
861
	 * @since  2.1.1
862
	 * @return string Label html markup
863
	 */
864 9
	public function label() {
865 9
		if ( ! $this->args( 'name' ) ) {
866
			return '';
867
		}
868
869 9
		$style = ! $this->args( 'show_names' ) ? ' style="display:none;"' : '';
870
871 9
		return sprintf( "\n" . '<label%1$s for="%2$s">%3$s</label>' . "\n", $style, $this->id(), $this->args( 'name' ) );
872
	}
873
874
	/**
875
	 * Defines the classes for the current CMB2 field row
876
	 *
877
	 * @since  2.0.0
878
	 * @return string Space concatenated list of classes
879
	 */
880 45
	public function row_classes() {
881
882 45
		$classes = array();
883
884
		/**
885
		 * By default, 'text_url' and 'text' fields get table-like styling
886
		 *
887
		 * @since 2.0.0
888
		 *
889
		 * @param array $field_types The types of fields which should get the 'table-layout' class
890
		 */
891 45
		$repeat_table_rows_types = apply_filters( 'cmb2_repeat_table_row_types', array(
892 45
			'text_url', 'text',
893 45
		) );
894
895
		$conditional_classes = array(
896 45
			'cmb-type-' . str_replace( '_', '-', sanitize_html_class( $this->type() ) ) => true,
897 45
			'cmb2-id-' . str_replace( '_', '-', sanitize_html_class( $this->id() ) )    => true,
898 45
			'cmb-repeat'             => $this->args( 'repeatable' ),
899 45
			'cmb-repeat-group-field' => $this->group,
900 45
			'cmb-inline'             => $this->args( 'inline' ),
901 45
			'table-layout'           => 'edit' === $this->render_context && in_array( $this->type(), $repeat_table_rows_types ),
902 45
		);
903
904 45
		foreach ( $conditional_classes as $class => $condition ) {
905 45
			if ( $condition ) {
906 45
				$classes[] = $class;
907 45
			}
908 45
		}
909
910 45
		if ( $added_classes = $this->get_param_callback_result( 'row_classes' ) ) {
911 3
			$added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes;
912 3
		}
913
914 45
		if ( $added_classes ) {
915 3
			$classes[] = esc_attr( $added_classes );
916 3
		}
917
918
		/**
919
		 * Globally filter row classes
920
		 *
921
		 * @since 2.0.0
922
		 *
923
		 * @param string            $classes Space-separated list of row classes
924
		 * @param CMB2_Field object $field   This field object
925
		 */
926 45
		return apply_filters( 'cmb2_row_classes', implode( ' ', $classes ), $this );
927
	}
928
929
930
931
	/**
932
	 * Get field display callback and render the display value in the column.
933
	 * @since 2.2.2
934
	 */
935 33
	public function render_column() {
936 33
		$this->render_context = 'display';
937
938 33
		$this->peform_param_callback( 'display_cb' );
939
940
		// For chaining
941 33
		return $this;
942
	}
943
944
	/**
945
	 * Default callback to outputs field value in a display format.
946
	 * @since 2.2.2
947
	 */
948 33
	public function display_value_callback() {
949
		// If field is requesting to be conditionally shown
950 33
		if ( ! $this->should_show() ) {
951
			return;
952
		}
953
954 33
		$display = new CMB2_Field_Display( $this );
955
956
		/**
957
		 * A filter to bypass the default display.
958
		 *
959
		 * The dynamic portion of the hook name, $this->type(), refers to the field type.
960
		 *
961
		 * Passing a non-null value to the filter will short-circuit the default display.
962
		 *
963
		 * @param bool|mixed         $pre_output Default null value.
964
		 * @param CMB2_Field         $field      This field object.
965
		 * @param CMB2_Field_Display $display    The `CMB2_Field_Display` object.
966
		 */
967 33
		$pre_output = apply_filters( "cmb2_pre_field_display_{$this->type()}", null, $this, $display );
968
969 33
		if ( null !== $pre_output ) {
970
			echo $pre_output;
971
			return;
972
		}
973
974 33
		$this->peform_param_callback( 'before_display_wrap' );
975
976 33
		printf( "<div class=\"cmb-column %s\" data-fieldtype=\"%s\">\n", $this->row_classes( 'display' ), $this->type() );
0 ignored issues
show
Unused Code introduced by
The call to CMB2_Field::row_classes() has too many arguments starting with 'display'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
977
978 33
		$this->peform_param_callback( 'before_display' );
979
980 33
		CMB2_Field_Display::get( $this )->display();
981
982 33
		$this->peform_param_callback( 'after_display' );
983
984 33
		echo "\n</div>";
985
986 33
		$this->peform_param_callback( 'after_display_wrap' );
987
988
		// For chaining
989 33
		return $this;
990
	}
991
992
	/**
993
	 * Determine whether this field should show, based on the 'show_on_cb' callback.
994
	 *
995
	 * @since 2.0.9
996
	 *
997
	 * @return bool Whether the field should be shown.
998
	 */
999 43 View Code Duplication
	public function should_show() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1000
		// Default to showing the field
1001 43
		$show = true;
1002
1003
		// Use the callback to determine showing the field, if it exists
1004 43
		if ( is_callable( $this->args( 'show_on_cb' ) ) ) {
1005 1
			$show = call_user_func( $this->args( 'show_on_cb' ), $this );
1006 1
		}
1007
1008 43
		return $show;
1009
	}
1010
1011
	/**
1012
	 * Displays the results of the param callbacks.
1013
	 *
1014
	 * @since 2.0.0
1015
	 * @param string $param Field parameter
1016
	 */
1017 80
	public function peform_param_callback( $param ) {
1018 80
		echo $this->get_param_callback_result( $param );
1019 80
	}
1020
1021
	/**
1022
	 * Store results of the param callbacks for continual access
1023
	 * @since  2.0.0
1024
	 * @param  string $param Field parameter
1025
	 * @return mixed         Results of param/param callback
1026
	 */
1027 85
	public function get_param_callback_result( $param ) {
1028
1029
		// If we've already retrieved this param's value,
1030 85
		if ( array_key_exists( $param, $this->callback_results ) ) {
1031
1032
			// send it back
1033 10
			return $this->callback_results[ $param ];
1034
		}
1035
1036
		// Check if parameter has registered a callback.
1037 85
		if ( $cb = $this->maybe_callback( $param ) ) {
1038
1039
			// Ok, callback is good, let's run it and store the result.
1040 49
			ob_start();
1041 49
			$returned = call_user_func( $cb, $this->args(), $this );
1042
1043
			// Grab the result from the output buffer and store it.
1044 49
			$echoed = ob_get_clean();
1045
1046
			// This checks if the user returned or echoed their callback.
1047
			// Defaults to using the echoed value.
1048 49
			$this->callback_results[ $param ] = $echoed ? $echoed : $returned;
1049
1050 49
		} else {
1051
1052
			// Otherwise just get whatever is there.
1053 80
			$this->callback_results[ $param ] = isset( $this->args[ $param ] ) ? $this->args[ $param ] : false;
1054
		}
1055
1056 85
		return $this->callback_results[ $param ];
1057
	}
1058
1059
	/**
1060
	 * Replaces a hash key - {#} - with the repeatable index
1061
	 * @since  1.2.0
1062
	 * @param  string $value Value to update
1063
	 * @return string        Updated value
1064
	 */
1065 2
	public function replace_hash( $value ) {
1066
		// Replace hash with 1 based count
1067 2
		return str_ireplace( '{#}', ( $this->index + 1 ), $value );
1068
	}
1069
1070
	/**
1071
	 * Retrieve text parameter from field's text array (if it has one), or use fallback text
1072
	 * For back-compatibility, falls back to checking the options array.
1073
	 *
1074
	 * @since  2.2.2
1075
	 * @param  string  $text_key Key in field's text array
1076
	 * @param  string  $fallback Fallback text
1077
	 * @return string            Text
1078
	 */
1079 8
	public function string( $text_key, $fallback ) {
1080
		// If null, populate with our field strings values.
1081 8
		if ( null === $this->strings ) {
1082 8
			$this->strings = (array) $this->args['text'];
1083
1084 8 View Code Duplication
			if ( is_callable( $this->args['text_cb'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1085
				$strings = call_user_func( $this->args['text_cb'], $this );
1086
1087
				if ( $strings && is_array( $strings ) ) {
1088
					$this->strings += $strings;
1089
				}
1090
			}
1091 8
		}
1092
1093
		// If we have that string value, send it back.
1094 8
		if ( isset( $this->strings[ $text_key ] ) ) {
1095 1
			return $this->strings[ $text_key ];
1096
		}
1097
1098
		// Check options for back-compat.
1099 8
		$string = $this->options( $text_key );
1100
1101 8
		return $string ? $string : $fallback;
0 ignored issues
show
Bug Compatibility introduced by
The expression $string ? $string : $fallback; of type array|string adds the type array to the return on line 1101 which is incompatible with the return type documented by CMB2_Field::string of type string.
Loading history...
1102
	}
1103
1104
	/**
1105
	 * Retrieve options args. Calls options_cb if it exists.
1106
	 * @since  2.0.0
1107
	 * @param  string  $key Specific option to retrieve
1108
	 * @return array        Array of options
1109
	 */
1110 31
	public function options( $key = '' ) {
1111 31
		if ( ! empty( $this->field_options ) ) {
1112 5
			if ( $key ) {
1113 5
				return array_key_exists( $key, $this->field_options ) ? $this->field_options[ $key ] : false;
1114
			}
1115
1116 1
			return $this->field_options;
1117
		}
1118
1119 31
		$this->field_options = (array) $this->args['options'];
1120
1121 31 View Code Duplication
		if ( is_callable( $this->args['options_cb'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1122 1
			$options = call_user_func( $this->args['options_cb'], $this );
1123
1124 1
			if ( $options && is_array( $options ) ) {
1125 1
				$this->field_options = $options + $this->field_options;
1126 1
			}
1127 1
		}
1128
1129 31
		if ( $key ) {
1130 10
			return array_key_exists( $key, $this->field_options ) ? $this->field_options[ $key ] : false;
1131
		}
1132
1133 23
		return $this->field_options;
1134
	}
1135
1136
	/**
1137
	 * Store JS dependencies as part of the field args.
1138
	 * @since 2.2.0
1139
	 * @param array $dependencies Dependies to register for this field.
1140
	 */
1141 12
	public function add_js_dependencies( $dependencies = array() ) {
1142 12
		foreach ( (array) $dependencies as $dependency ) {
1143 12
			$this->args['js_dependencies'][ $dependency ] = $dependency;
1144 12
		}
1145
1146 12
		CMB2_JS::add_dependencies( $dependencies );
1147 12
	}
1148
1149
	/**
1150
	 * Get CMB2_Field default value, either from default param or default_cb param.
1151
	 *
1152
	 * @since  0.2.2
1153
	 *
1154
	 * @return mixed  Default field value
1155
	 */
1156 33
	public function get_default() {
1157 33
		if ( null !== $this->args['default'] ) {
1158 15
			return $this->args['default'];
1159
		}
1160
1161 32
		$param = is_callable( $this->args['default_cb'] ) ? 'default_cb' : 'default';
1162 32
		$default = $this->get_param_callback_result( $param );
1163
1164
		// Allow a filter override of the default value
1165 32
		$this->args['default'] = apply_filters( 'cmb2_default_filter', $default, $this );
1166
1167 32
		return $this->args['default'];
1168
	}
1169
1170
	/**
1171
	 * Fills in empty field parameters with defaults
1172
	 * @since 1.1.0
1173
	 * @param array $args Metabox field config array
1174
	 */
1175 107
	public function _set_field_defaults( $args ) {
1176
1177
		// Set up blank or default values for empty ones
1178 107
		$args = wp_parse_args( $args, array(
1179 107
			'type'              => '',
1180 107
			'name'              => '',
1181 107
			'desc'              => '',
1182 107
			'before'            => '',
1183 107
			'after'             => '',
1184 107
			'options'           => array(),
1185 107
			'options_cb'        => '',
1186 107
			'text'              => array(),
1187 107
			'text_cb'           => '',
1188 107
			'attributes'        => array(),
1189 107
			'protocols'         => null,
1190 107
			'default'           => null,
1191 107
			'default_cb'        => '',
1192 107
			'select_all_button' => true,
1193 107
			'multiple'          => false,
1194 107
			'repeatable'        => isset( $args['type'] ) && 'group' == $args['type'],
1195 107
			'inline'            => false,
1196 107
			'on_front'          => true,
1197 107
			'show_names'        => true,
1198 107
			'date_format'       => 'm\/d\/Y',
1199 107
			'time_format'       => 'h:i A',
1200 107
			'description'       => isset( $args['desc'] ) ? $args['desc'] : '',
1201 107
			'preview_size'      => 'file' == $args['type'] ? array( 350, 350 ) : array( 50, 50 ),
1202 107
			'render_row_cb'     => array( $this, 'render_field_callback' ),
1203 107
			'display_cb'        => array( $this, 'display_value_callback' ),
1204 107
			'label_cb'          => 'title' != $args['type'] ? array( $this, 'label' ) : '',
1205 107
			'column'            => false,
1206 107
			'show_in_rest'      => null,
1207 107
			'js_dependencies'   => array(),
1208 107
		) );
1209
1210
		// default param can be passed a callback as well
1211 107
		if ( is_callable( $args['default'] ) ) {
1212 19
			$args['default_cb'] = $args['default'];
1213 19
			$args['default'] = null;
1214 19
		}
1215
1216 107
		$args['repeatable'] = $args['repeatable'] && ! $this->repeatable_exception( $args['type'] );
1217 107
		$args['inline']     = $args['inline'] || false !== stripos( $args['type'], '_inline' );
1218
1219
		// options param can be passed a callback as well
1220 107
		if ( is_callable( $args['options'] ) ) {
1221
			$args['options_cb'] = $args['options'];
1222
			$args['options'] = array();
1223
		}
1224
1225 107
		$args['options']    = 'group' == $args['type'] ? wp_parse_args( $args['options'], array(
1226 3
			'add_button'    => __( 'Add Group', 'cmb2' ),
1227 3
			'remove_button' => __( 'Remove Group', 'cmb2' ),
1228 107
		) ) : $args['options'];
1229
1230 107
		$args['_id']        = $args['id'];
1231 107
		$args['_name']      = $args['id'];
1232
1233 107
		if ( $this->group ) {
1234
1235 3
			$args['id']    = $this->group->args( 'id' ) . '_' . $this->group->index . '_' . $args['id'];
1236 3
			$args['_name'] = $this->group->args( 'id' ) . '[' . $this->group->index . '][' . $args['_name'] . ']';
1237 3
		}
1238
1239 107
		if ( 'wysiwyg' == $args['type'] ) {
1240 1
			$args['id'] = strtolower( str_ireplace( '-', '_', $args['id'] ) );
1241 1
			$args['options']['textarea_name'] = $args['_name'];
1242 1
		}
1243
1244 107
		$option_types = apply_filters( 'cmb2_all_or_nothing_types', array( 'select', 'radio', 'radio_inline', 'taxonomy_select', 'taxonomy_radio', 'taxonomy_radio_inline' ), $this );
1245
1246 107
		if ( in_array( $args['type'], $option_types, true ) ) {
1247
1248 16
			$args['show_option_none'] = isset( $args['show_option_none'] ) ? $args['show_option_none'] : null;
1249 16
			$args['show_option_none'] = true === $args['show_option_none'] ? __( 'None', 'cmb2' ) : $args['show_option_none'];
1250
1251 16
			if ( null === $args['show_option_none'] ) {
1252 15
				$off_by_default = in_array( $args['type'], array( 'select', 'radio', 'radio_inline' ), true );
1253 15
				$args['show_option_none'] = $off_by_default ? false : __( 'None', 'cmb2' );
1254 15
			}
1255
1256 16
		}
1257
1258 107
		$args['has_supporting_data'] = in_array(
1259 107
			$args['type'],
1260
			array(
1261
				// CMB2_Sanitize::_save_file_id_value()/CMB2_Sanitize::_get_group_file_value_array()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1262 107
				'file',
1263
				// See CMB2_Sanitize::_save_utc_value()
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1264 107
				'text_datetime_timestamp_timezone',
1265 107
			),
1266
			true
1267 107
		);
1268
1269 107
		return $args;
1270
	}
1271
1272
	/**
1273
	 * Returns a cloned version of this field object with, but with
1274
	 * modified/overridden field arguments.
1275
	 *
1276
	 * @since  2.2.2
1277
	 * @param  array  $field_args Array of field arguments, or entire array of
1278
	 *                            arguments for CMB2_Field
1279
	 *
1280
	 * @return CMB2_Field         The new CMB2_Field instance.
1281
	 */
1282 5
	public function get_field_clone( $field_args ) {
1283
		$args = array(
1284 5
			'field_args'  => array(),
1285 5
			'group_field' => $this->group,
1286 5
			'object_id'   => $this->object_id,
1287 5
			'object_type' => $this->object_type,
1288 5
			'cmb_id'      => $this->cmb_id,
1289 5
		);
1290
1291 5
		if ( isset( $field_args['field_args'] ) ) {
1292
			$args = wp_parse_args( $field_args, $args );
1293
		} else {
1294 5
			$args['field_args'] = wp_parse_args( $field_args, $this->args );
1295
		}
1296
1297 5
		return new CMB2_Field( $args );
1298
	}
1299
1300
	/**
1301
	 * Returns the CMB2 instance this field is registered to.
1302
	 *
1303
	 * @since  2.2.2
1304
	 *
1305
	 * @return CMB2|WP_Error If new CMB2_Field is called without cmb_id arg, returns error.
1306
	 */
1307 1
	public function get_cmb() {
1308 1
		if ( ! $this->cmb_id ) {
1309
			return new WP_Error( 'no_cmb_id', __( 'Sorry, this field does not have a cmb_id specified.', 'cmb2' ) );
1310
		}
1311
1312 1
		return cmb2_get_metabox( $this->cmb_id, $this->object_id, $this->object_type );
1313
	}
1314
1315
}
1316