Completed
Pull Request — trunk (#541)
by Justin
03:11
created

CMB2_Field::save_field_from_data()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 9
ccs 1
cts 1
cp 1
crap 2
rs 9.6666
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 extends CMB2_Base {
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
	 * The object properties name.
21
	 * @var   string
22
	 * @since 2.2.3
23
	 */
24
	protected $properties_name = 'args';
25
26
	/**
27
	 * Field arguments
28
	 * @var   mixed
29
	 * @since 1.1.0
30
	 */
31
	public $args = array();
32
33
	/**
34
	 * Field group object or false (if no group)
35
	 * @var   mixed
36
	 * @since 1.1.0
37
	 */
38
	public $group = false;
39
40
	/**
41
	 * Field meta value
42
	 * @var   mixed
43
	 * @since 1.1.0
44
	 */
45
	public $value = null;
46
47
	/**
48
	 * Field meta value
49 46
	 * @var   mixed
50
	 * @since 1.1.0
51 46
	 */
52
	public $escaped_value = null;
53
54
	/**
55
	 * Grouped Field's current numeric index during the save process
56 46
	 * @var   mixed
57 46
	 * @since 2.0.0
58 46
	 */
59
	public $index = 0;
60
61 46
	/**
62
	 * Array of field options
63
	 * @var   array
64
	 * @since 2.0.0
65
	 */
66
	protected $field_options = array();
67
68
	/**
69
	 * Array of provided field text strings
70
	 * @var   array
71
	 * @since 2.0.0
72
	 */
73
	protected $strings;
74
75
	/**
76
	 * The field's render context. In most cases, 'edit', but can be 'display'.
77
	 * @var   string
78 46
	 * @since 2.2.2
79
	 */
80
	public $render_context = 'edit';
81
82
	/**
83
	 * All CMB2_Field callable field arguments.
84
	 * Can be used to determine if a field argument is callable.
85
	 *
86
	 * @var array
87
	 */
88
	public static $callable_fields = array(
89
		'default',
90
		'row_classes',
91
		'options_cb',
92
		'label_cb',
93
		'render_row_cb',
94
		'before_group',
95
		'before_group_row',
96
		'before_row',
97 46
		'before',
98
		'before_field',
99
		'after_field',
100 46
		'after',
101 46
		'after_row',
102 46
		'after_group_row',
103 46
		'after_group',
104
	);
105
106
	/**
107
	 * Constructs our field object
108
	 * @since 1.1.0
109
	 * @param array $args Field arguments
110
	 */
111
	public function __construct( $args ) {
112 38
113 38
		if ( ! empty( $args['group_field'] ) ) {
114 38
			$this->group       = $args['group_field'];
115
			$this->object_id   = $this->group->object_id;
116
			$this->object_type = $this->group->object_type;
117
			$this->cmb_id      = $this->group->cmb_id;
118
		} else {
119
			$this->object_id   = isset( $args['object_id'] ) && '_' !== $args['object_id'] ? $args['object_id'] : 0;
120
			$this->object_type = isset( $args['object_type'] ) ? $args['object_type'] : 'post';
121
122
			if ( isset( $args['cmb_id'] ) ) {
123 46
				$this->cmb_id = $args['cmb_id'];
124 46
			}
125 46
		}
126
127
		$this->args = $this->_set_field_defaults( $args['field_args'], $args );
0 ignored issues
show
Unused Code introduced by
The call to CMB2_Field::_set_field_defaults() has too many arguments starting with $args.

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...
128
129
		if ( $this->object_id ) {
130
			$this->value = $this->get_data();
131
		}
132
	}
133
134
	/**
135 46
	 * Non-existent methods fallback to checking for field arguments of the same name
136 46
	 * @since  1.1.0
137 46
	 * @param  string $name     Method name
138
	 * @param  array  $arguments Array of passed-in arguments
139
	 * @return mixed             Value of field argument
140 46
	 */
141
	public function __call( $name, $arguments ) {
142
		if ( 'string' === $name ) {
143
			return call_user_func_array( array( $this, 'get_string' ), $arguments );
144
		}
145
146
		$key = isset( $arguments[0] ) ? $arguments[0] : false;
147
		return $this->args( $name, $key );
148
	}
149 37
150 37
	/**
151
	 * Retrieves the field id
152
	 * @since  1.1.0
153
	 * @param  boolean $raw Whether to retrieve pre-modidifed id
154
	 * @return string       Field id
155
	 */
156
	public function id( $raw = false ) {
157
		$id = $raw ? '_id' : 'id';
158
		return $this->args( $id );
159
	}
160 46
161 46
	/**
162 46
	 * Get a field argument
163 46
	 * @since  1.1.0
164
	 * @param  string $key  Argument to check
165 46
	 * @param  string $_key Sub argument to check
166
	 * @return mixed        Argument value or false if non-existent
167
	 */
168
	public function args( $key = '', $_key = '' ) {
169
		$arg = $this->_data( 'args', $key );
170
171
		if ( in_array( $key, array( 'default', 'default_cb' ), true ) ) {
172
173
			$arg = $this->get_default();
174 46
175 46
		} elseif ( $_key ) {
176
177 46
			$arg = isset( $arg[ $_key ] ) ? $arg[ $_key ] : false;
178
		}
179
180
		return $arg;
181 46
	}
182
183 46
	/**
184 46
	 * Retrieve a portion of a field property
185 46
	 * @since  1.1.0
186
	 * @param  string  $var Field property to check
187 46
	 * @param  string  $key Field property array key to check
188
	 * @return mixed        Queried property value or false
189
	 */
190
	public function _data( $var, $key = '' ) {
191
		$vars = $this->{$var};
192 46
		if ( $key ) {
193
			return array_key_exists( $key, $vars ) ? $vars[ $key ] : false;
194
		}
195
		return $vars;
196
	}
197
198
	/**
199
	 * Get Field's value
200
	 * @since  1.1.0
201
	 * @param  string $key If value is an array, is used to get array key->value
202
	 * @return mixed       Field value or false if non-existent
203
	 */
204
	public function value( $key = '' ) {
205
		return $this->_data( 'value', $key );
206
	}
207
208
	/**
209
	 * Retrieves metadata/option data
210
	 * @since  1.0.1
211
	 * @param  string $field_id Meta key/Option array key
212
	 * @param  array  $args     Override arguments
213
	 * @return mixed            Meta/Option value
214
	 */
215
	public function get_data( $field_id = '', $args = array() ) {
216
		if ( $field_id ) {
217
			$args['field_id'] = $field_id;
218
		} else if ( $this->group ) {
219
			$args['field_id'] = $this->group->id();
220
		}
221
222
		$a = $this->data_args( $args );
223
224
		/**
225
		 * Filter whether to override getting of meta value.
226
		 * Returning a non 'cmb2_field_no_override_val' value
227
		 * will effectively short-circuit the value retrieval.
228
		 *
229
		 * @since 2.0.0
230
		 *
231
		 * @param mixed $value     The value get_metadata() should
232
		 *                         return - a single metadata value,
233
		 *                         or an array of values.
234
		 *
235
		 * @param int   $object_id Object ID.
236
		 *
237
		 * @param array $args {
238
		 *     An array of arguments for retrieving data
239
		 *
240
		 *     @type string $type     The current object type
241
		 *     @type int    $id       The current object ID
242
		 *     @type string $field_id The ID of the field being requested
243
		 *     @type bool   $repeat   Whether current field is repeatable
244
		 *     @type bool   $single   Whether current field is a single database row
245
		 * }
246
		 *
247
		 * @param CMB2_Field object $field This field object
248
		 */
249
		$data = apply_filters( 'cmb2_override_meta_value', 'cmb2_field_no_override_val', $this->object_id, $a, $this );
250
251
		/**
252
		 * Filter and parameters are documented for 'cmb2_override_meta_value' filter (above).
253
		 *
254
		 * The dynamic portion of the hook, $field_id, refers to the current
255
		 * field id paramater. Returning a non 'cmb2_field_no_override_val' value
256
		 * will effectively short-circuit the value retrieval.
257
		 *
258
		 * @since 2.0.0
259
		 */
260
		$data = apply_filters( "cmb2_override_{$a['field_id']}_meta_value", $data, $this->object_id, $a, $this );
261
262
		// If no override, get value normally
263
		if ( 'cmb2_field_no_override_val' === $data ) {
264
			$data = 'options-page' === $a['type']
265
				? cmb2_options( $a['id'] )->get( $a['field_id'] )
266
				: get_metadata( $a['type'], $a['id'], $a['field_id'], ( $a['single'] || $a['repeat'] ) );
267
		}
268
269
		if ( $this->group ) {
270
271
			$data = is_array( $data ) && isset( $data[ $this->group->index ][ $this->args( '_id' ) ] )
272
				? $data[ $this->group->index ][ $this->args( '_id' ) ]
273
				: false;
274
		}
275
276
		return $data;
277
	}
278
279
	/**
280
	 * Updates metadata/option data
281
	 * @since  1.0.1
282
	 * @param  mixed $new_value Value to update data with
283
	 * @param  bool  $single    Whether data is an array (add_metadata)
284
	 */
285
	public function update_data( $new_value, $single = true ) {
286
		$a = $this->data_args( array( 'single' => $single ) );
287
288
		$a['value'] = $a['repeat'] ? array_values( $new_value ) : $new_value;
289
290
		/**
291
		 * Filter whether to override saving of meta value.
292
		 * Returning a non-null value will effectively short-circuit the function.
293
		 *
294
		 * @since 2.0.0
295
		 *
296
		 * @param null|bool $check  Whether to allow updating metadata for the given type.
297
		 *
298
		 * @param array $args {
299
		 *     Array of data about current field including:
300
		 *
301
		 *     @type string $value    The value to set
302
		 *     @type string $type     The current object type
303
		 *     @type int    $id       The current object ID
304
		 *     @type string $field_id The ID of the field being updated
305
		 *     @type bool   $repeat   Whether current field is repeatable
306
		 *     @type bool   $single   Whether current field is a single database row
307
		 * }
308
		 *
309
		 * @param array $field_args All field arguments
310
		 *
311
		 * @param CMB2_Field object $field This field object
312
		 */
313
		$override = apply_filters( 'cmb2_override_meta_save', null, $a, $this->args(), $this );
314
315
		/**
316
		 * Filter and parameters are documented for 'cmb2_override_meta_save' filter (above).
317
		 *
318
		 * The dynamic portion of the hook, $a['field_id'], refers to the current
319
		 * field id paramater. Returning a non-null value
320
		 * will effectively short-circuit the function.
321
		 *
322
		 * @since 2.0.0
323
		 */
324
		$override = apply_filters( "cmb2_override_{$a['field_id']}_meta_save", $override, $a, $this->args(), $this );
325
326
		// If override, return that
327
		if ( null !== $override ) {
328
			return $override;
329
		}
330
331
		// Options page handling (or temp data store)
332
		if ( 'options-page' === $a['type'] || empty( $a['id'] ) ) {
333
			return cmb2_options( $a['id'] )->update( $a['field_id'], $a['value'], false, $a['single'] );
334
		}
335
336
		// Add metadata if not single
337
		if ( ! $a['single'] ) {
338
			return add_metadata( $a['type'], $a['id'], $a['field_id'], $a['value'], false );
339
		}
340
341
		// Delete meta if we have an empty array
342
		if ( is_array( $a['value'] ) && empty( $a['value'] ) ) {
343
			return delete_metadata( $a['type'], $a['id'], $a['field_id'], $this->value );
344
		}
345
346
		// Update metadata
347
		return update_metadata( $a['type'], $a['id'], $a['field_id'], $a['value'] );
348
	}
349
350 46
	/**
351 46
	 * Removes/updates metadata/option data
352 46
	 * @since  1.0.1
353 46
	 * @param  string $old Old value
354 46
	 */
355 46
	public function remove_data( $old = '' ) {
356 46
		$a = $this->data_args( array( 'old' => $old ) );
357 46
358 46
		/**
359
		 * Filter whether to override removing of meta value.
360
		 * Returning a non-null value will effectively short-circuit the function.
361
		 *
362
		 * @since 2.0.0
363
		 *
364
		 * @param null|bool $delete Whether to allow metadata deletion of the given type.
365
		 * @param array $args       Array of data about current field including:
366
		 *                              'type'     : Current object type
367
		 *                              'id'       : Current object ID
368
		 *                              'field_id' : Current Field ID
369
		 *                              'repeat'   : Whether current field is repeatable
370
		 *                              'single'   : Whether to save as a
371
		 *                              					single meta value
372
		 * @param array $field_args All field arguments
373
		 * @param CMB2_Field object $field This field object
374
		 */
375
		$override = apply_filters( 'cmb2_override_meta_remove', null, $a, $this->args(), $this );
376
377
		/**
378
		 * Filter whether to override removing of meta value.
379
		 *
380
		 * The dynamic portion of the hook, $a['field_id'], refers to the current
381
		 * field id paramater. Returning a non-null value
382
		 * will effectively short-circuit the function.
383
		 *
384
		 * @since 2.0.0
385
		 *
386
		 * @param null|bool $delete Whether to allow metadata deletion of the given type.
387
		 * @param array $args       Array of data about current field including:
388
		 *                              'type'     : Current object type
389
		 *                              'id'       : Current object ID
390
		 *                              'field_id' : Current Field ID
391
		 *                              'repeat'   : Whether current field is repeatable
392
		 *                              'single'   : Whether to save as a
393
		 *                              					single meta value
394
		 * @param array $field_args All field arguments
395
		 * @param CMB2_Field object $field This field object
396
		 */
397
		$override = apply_filters( "cmb2_override_{$a['field_id']}_meta_remove", $override, $a, $this->args(), $this );
398
399
		// If no override, remove as usual
400
		if ( null !== $override ) {
401
			return $override;
402
		}
403
		// Option page handling
404
		elseif ( 'options-page' === $a['type'] || empty( $a['id'] ) ) {
405
			return cmb2_options( $a['id'] )->remove( $a['field_id'] );
406
		}
407
408
		// Remove metadata
409
		return delete_metadata( $a['type'], $a['id'], $a['field_id'], $old );
410
	}
411
412
	/**
413
	 * Data variables for get/set data methods
414
	 * @since  1.1.0
415
	 * @param  array $args Override arguments
416
	 * @return array       Updated arguments
417
	 */
418
	public function data_args( $args = array() ) {
419
		$args = wp_parse_args( $args, array(
420
			'type'     => $this->object_type,
421
			'id'       => $this->object_id,
422
			'field_id' => $this->id( true ),
423
			'repeat'   => $this->args( 'repeatable' ),
424
			'single'   => ! $this->args( 'multiple' ),
425
		) );
426
		return $args;
427
	}
428
429 39
	/**
430 39
	 * Checks if field has a registered sanitization callback
431 39
	 * @since  1.0.1
432 38
	 * @param  mixed $meta_value Meta value
433
	 * @return mixed             Possibly sanitized meta value
434
	 */
435
	public function sanitization_cb( $meta_value ) {
436 7
437
		if ( $this->args( 'repeatable' ) && is_array( $meta_value ) ) {
438
			// Remove empties
439 7
			$meta_value = array_filter( $meta_value );
440 4
		}
441
442
		// Check if the field has a registered validation callback
443 3
		$cb = $this->maybe_callback( 'sanitization_cb' );
444 2
		if ( false === $cb ) {
445
			// If requesting NO validation, return meta value
446 3
			return $meta_value;
447
		} elseif ( $cb ) {
448
			// Ok, callback is good, let's run it.
449
			return call_user_func( $cb, $meta_value, $this->args(), $this );
450
		}
451
452
		$sanitizer = new CMB2_Sanitize( $this, $meta_value );
453 37
454
		/**
455 37
		 * Filter the value before it is saved.
456 37
		 *
457 37
		 * The dynamic portion of the hook name, $this->type(), refers to the field type.
458 37
		 *
459 37
		 * Passing a non-null value to the filter will short-circuit saving
460
		 * the field value, saving the passed value instead.
461
		 *
462
		 * @param bool|mixed $override_value Sanitization/Validation override value to return.
463
		 *                                   Default false to skip it.
464
		 * @param mixed      $value      The value to be saved to this field.
465
		 * @param int        $object_id  The ID of the object where the value will be saved
466
		 * @param array      $field_args The current field's arguments
467
		 * @param object     $sanitizer  This `CMB2_Sanitize` object
468 1
		 */
469
		$override_value = apply_filters( "cmb2_sanitize_{$this->type()}", null, $sanitizer->value, $this->object_id, $this->args(), $sanitizer );
470 1
471 1
		if ( null !== $override_value ) {
472 1
			return $override_value;
473 1
		}
474 1
475
		// Sanitization via 'CMB2_Sanitize'
476 1
		return $sanitizer->{$this->type()}();
477 1
	}
478 1
479 1
	/**
480 1
	 * Process $_POST data to save this field's value
481 1
	 * @since  2.0.3
482 1
	 * @param  array $data_to_save $_POST data to check
483
	 * @return array|int|bool                Result of save, false on failure
484
	 */
485
	public function save_field_from_data( array $data_to_save ) {
486
		$this->data_to_save = $data_to_save;
487
488
		$meta_value = isset( $this->data_to_save[ $this->id( true ) ] )
489
			? $this->data_to_save[ $this->id( true ) ]
490
			: null;
491
492 37
		return $this->save_field( $meta_value );
493
	}
494 37
495 16
	/**
496
	 * Sanitize/store a value to this field
497
	 * @since  2.0.0
498 37
	 * @param  array $meta_value Desired value to sanitize/store
499
	 * @return array|int|bool              Result of save. false on failure
500
	 */
501 37
	public function save_field( $meta_value ) {
502
503
		$updated   = false;
504
		$action    = '';
505
		$new_value = $this->sanitization_cb( $meta_value );
506
507 37
		if ( ! $this->args( 'save_field' ) ) {
508 37
509
			// Nothing to see here.
510
			$action = 'disabled';
511
512 37
		} elseif ( $this->args( 'multiple' ) && ! $this->args( 'repeatable' ) && ! $this->group ) {
513
514 4
			$this->remove_data();
515
			$count = 0;
516
517
			if ( ! empty( $new_value ) ) {
518 33
				foreach ( $new_value as $add_new ) {
519 33
					if ( $this->update_data( $add_new, false ) ) {
520
						$count++;
521 33
					}
522
				}
523
			}
524
525
			$updated = $count ? $count : false;
526 33
			$action  = 'repeatable';
527
528
		} elseif ( ! CMB2_Utils::isempty( $new_value ) && $new_value !== $this->get_data() ) {
529 33
			$updated = $this->update_data( $new_value );
530 33
			$action  = 'updated';
531
		} elseif ( CMB2_Utils::isempty( $new_value ) ) {
532
			$updated = $this->remove_data();
533
			$action  = 'removed';
534
		}
535
536
		if ( $updated ) {
537
			$this->value = $this->get_data();
538 1
			$this->escaped_value = null;
539 1
		}
540
541
		$field_id = $this->id( true );
542
543
		/**
544
		 * Hooks after save field action.
545
		 *
546
		 * @since 2.2.0
547 1
		 *
548
		 * @param string            $field_id the current field id paramater.
549
		 * @param bool              $updated  Whether the metadata update action occurred.
550 1
		 * @param string            $action   Action performed. Could be "repeatable", "updated", or "removed".
551
		 * @param CMB2_Field object $field    This field object
552
		 */
553
		do_action( 'cmb2_save_field', $field_id, $updated, $action, $this );
554 1
555
		/**
556
		 * Hooks after save field action.
557
		 *
558 1
		 * The dynamic portion of the hook, $field_id, refers to the
559
		 * current field id paramater.
560
		 *
561
		 * @since 2.2.0
562
		 *
563
		 * @param bool              $updated Whether the metadata update action occurred.
564
		 * @param string            $action  Action performed. Could be "repeatable", "updated", or "removed".
565
		 * @param CMB2_Field object $field   This field object
566
		 */
567
		do_action( "cmb2_save_field_{$field_id}", $updated, $action, $this );
568 5
569 5
		return $updated;
570
	}
571
572
	/**
573
	 * Determine if current type is exempt from escaping
574
	 * @since  1.1.0
575
	 * @return bool  True if exempt
576
	 */
577
	public function escaping_exception() {
578 5
		// These types cannot be escaped
579 5
		return in_array( $this->type(), array(
580 5
			'file_list',
581
			'multicheck',
582 5
			'text_datetime_timestamp_timezone',
583
		) );
584
	}
585
586 5
	/**
587 5
	 * Determine if current type cannot be repeatable
588 5
	 * @since  1.1.0
589
	 * @param  string $type Field type to check
590
	 * @return bool         True if type cannot be repeatable
591
	 */
592
	public function repeatable_exception( $type ) {
593
		// These types cannot be repeatable.
594
		$internal_fields = array(
595 5
			// Use file_list instead
596
			'file'                => 1,
597
			'radio'               => 1,
598 5
			'title'               => 1,
599
			// @todo Ajax load wp_editor: http://wordpress.stackexchange.com/questions/51776/how-to-load-wp-editor-through-ajax-jquery
600
			'wysiwyg'             => 1,
601
			'checkbox'            => 1,
602
			'radio_inline'        => 1,
603 5
			'taxonomy_radio'      => 1,
604
			'taxonomy_select'     => 1,
605
			'taxonomy_multicheck' => 1,
606
		);
607 5
608
		/**
609 5
		 * Filter field types that are non-repeatable.
610
		 *
611 5
		 * Note that this does *not* allow overriding the default non-repeatable types.
612
		 *
613
		 * @since 2.1.1
614
		 *
615
		 * @param array $fields Array of fields designated as non-repeatable. Note that the field names are *keys*,
616
		 *                      and not values. The value can be anything, because it is meaningless. Example:
617
		 *                      array( 'my_custom_field' => 1 )
618
		 */
619
		$all_fields = array_merge( apply_filters( 'cmb2_non_repeatable_fields', array() ), $internal_fields );
620 5
		return isset( $all_fields[ $type ] );
621 5
	}
622 5
623
	/**
624 5
	 * Escape the value before output. Defaults to 'esc_attr()'
625
	 * @since  1.0.1
626
	 * @param  callable $func       Escaping function (if not esc_attr())
627 5
	 * @param  mixed    $meta_value Meta value
628
	 * @return mixed                Final value
629 5
	 */
630 5
	public function escaped_value( $func = 'esc_attr', $meta_value = '' ) {
631
632 5
		if ( null !== $this->escaped_value ) {
633
			return $this->escaped_value;
634 5
		}
635
636 5
		$meta_value = $meta_value ? $meta_value : $this->value();
637 5
638
		// Check if the field has a registered escaping callback
639
		if ( $cb = $this->maybe_callback( 'escape_cb' ) ) {
640
			// Ok, callback is good, let's run it.
641
			return call_user_func( $cb, $meta_value, $this->args(), $this );
642
		}
643
644
		// Or custom escaping filter can be used
645 5
		$esc = apply_filters( "cmb2_types_esc_{$this->type()}", null, $meta_value, $this->args(), $this );
646 5
		if ( null !== $esc ) {
647 5
			return $esc;
648 5
		}
649 5
650
		if ( false === $cb || $this->escaping_exception() ) {
651 5
			// If requesting NO escaping, return meta value
652
			return $this->val_or_default( $meta_value );
653 5
		}
654 5
655 5
		// escaping function passed in?
656
		$func       = $func ? $func : 'esc_attr';
657 5
		$meta_value = $this->val_or_default( $meta_value );
658 3
659 3
		if ( is_array( $meta_value ) ) {
660
			foreach ( $meta_value as $key => $value ) {
661 5
				$meta_value[ $key ] = call_user_func( $func, $value );
662
			}
663
		} else {
664
			$meta_value = call_user_func( $func, $meta_value );
665
		}
666
667
		$this->escaped_value = $meta_value;
668
		return $this->escaped_value;
669
	}
670
671 38
	/**
672 38
	 * Return non-empty value or field default if value IS empty
673
	 * @since  2.0.0
674 2
	 * @param  mixed $meta_value Field value
675 2
	 * @return mixed             Field value, or default value
676
	 */
677
	public function val_or_default( $meta_value ) {
678
		return ! CMB2_Utils::isempty( $meta_value ) ? $meta_value : $this->get_default();
679 38
	}
680 38
681
	/**
682
	 * Offset a time value based on timezone
683
	 * @since  1.0.0
684
	 * @return string Offset time string
685
	 */
686
	public function field_timezone_offset() {
687
		return CMB2_Utils::timezone_offset( $this->field_timezone() );
688
	}
689
690
	/**
691
	 * Return timezone string
692
	 * @since  1.0.0
693
	 * @return string Timezone string
694
	 */
695
	public function field_timezone() {
696
		$value = '';
697
698
		// Is timezone arg set?
699 14
		if ( $this->args( 'timezone' ) ) {
700 14
			$value = $this->args( 'timezone' );
701 5
		}
702 4
		// Is there another meta key with a timezone stored as its value we should use?
703
		else if ( $this->args( 'timezone_meta_key' ) ) {
704
			$value = $this->get_data( $this->args( 'timezone_meta_key' ) );
705 1
		}
706
707
		return $value;
708 14
	}
709
710 14
	/**
711 1
	 * Format the timestamp field value based on the field date/time format arg
712
	 * @since  2.0.0
713 1
	 * @param  int    $meta_value Timestamp
714 1
	 * @param  string $format     Either date_format or time_format
715 1
	 * @return string             Formatted date
716 1
	 */
717
	public function format_timestamp( $meta_value, $format = 'date_format' ) {
718 14
		return date( stripslashes( $this->args( $format ) ), $meta_value );
719 4
	}
720
721
	/**
722 11
	 * Return a formatted timestamp for a field
723
	 * @since  2.0.0
724
	 * @param  string $format     Either date_format or time_format
725
	 * @param  string $meta_value Optional meta value to check
726
	 * @return string             Formatted date
727
	 */
728
	public function get_timestamp_format( $format = 'date_format', $meta_value = 0 ) {
729
		$meta_value = $meta_value ? $meta_value : $this->escaped_value();
730 46
		$meta_value = CMB2_Utils::make_valid_time_stamp( $meta_value );
731
732
		if ( empty( $meta_value ) ) {
733 46
			return '';
734 46
		}
735 46
736 46
		return is_array( $meta_value )
737 46
			? array_map( array( $this, 'format_timestamp' ), $meta_value, $format )
738 46
			: $this->format_timestamp( $meta_value, $format );
739 46
	}
740 46
741 46
	/**
742 46
	 * Get timestamp from text date
743 46
	 * @since  2.2.0
744 46
	 * @param  string $value Date value
745 46
	 * @return mixed         Unix timestamp representing the date.
746 46
	 */
747 46
	public function get_timestamp_from_value( $value ) {
748 46
		return CMB2_Utils::get_timestamp_from_value( $value, $this->args( 'date_format' ) );
749 46
	}
750 46
751 46
	/**
752 46
	 * Get field render callback and Render the field row
753 46
	 * @since 1.0.0
754 46
	 */
755
	public function render_field() {
756
		$this->render_context = 'edit';
757
758 46
		$this->peform_param_callback( 'render_row_cb' );
759
760 46
		// For chaining
761 46
		return $this;
762
	}
763
764 46
	/**
765
	 * Default field render callback
766
	 * @since 2.1.1
767
	 */
768
	public function render_field_callback() {
769 46
770
		// If field is requesting to not be shown on the front-end
771
		if ( ! is_admin() && ! $this->args( 'on_front' ) ) {
772 46
			return;
773
		}
774 46
775 46
		// If field is requesting to be conditionally shown
776
		if ( ! $this->should_show() ) {
777 46
			return;
778
		}
779
780
		$this->peform_param_callback( 'before_row' );
781
782
		printf( "<div class=\"cmb-row %s\" data-fieldtype=\"%s\">\n", $this->row_classes(), $this->type() );
783 46
784 1
		if ( ! $this->args( 'show_names' ) ) {
785 1
			echo "\n\t<div class=\"cmb-td\">\n";
786 1
787
			$this->peform_param_callback( 'label_cb' );
788 46
789
		} else {
790 46
791
			if ( $this->get_param_callback_result( 'label_cb' ) ) {
792 2
				echo '<div class="cmb-th">', $this->peform_param_callback( 'label_cb' ), '</div>';
793 2
			}
794
795 2
			echo "\n\t<div class=\"cmb-td\">\n";
796
		}
797 46
798
		$this->peform_param_callback( 'before' );
799
800
		$field_type = new CMB2_Types( $this );
801
		$field_type->render();
802
803
		$this->peform_param_callback( 'after' );
804
805 36
		echo "\n\t</div>\n</div>";
806 36
807
		$this->peform_param_callback( 'after_row' );
808
809
		// For chaining
810 1
		return $this;
811
	}
812
813
	/**
814
	 * The default label_cb callback (if not a title field)
815
	 *
816
	 * @since  2.1.1
817
	 * @return string Label html markup
818
	 */
819
	public function label() {
820
		if ( ! $this->args( 'name' ) ) {
821
			return '';
822
		}
823
824
		$style = ! $this->args( 'show_names' ) ? ' style="display:none;"' : '';
825
826
		return sprintf( "\n" . '<label%1$s for="%2$s">%3$s</label>' . "\n", $style, $this->id(), $this->args( 'name' ) );
827
	}
828
829
	/**
830
	 * Defines the classes for the current CMB2 field row
831
	 *
832
	 * @since  2.0.0
833
	 * @return string Space concatenated list of classes
834
	 */
835
	public function row_classes() {
836
837
		$classes = array();
838
839
		/**
840
		 * By default, 'text_url' and 'text' fields get table-like styling
841
		 *
842
		 * @since 2.0.0
843
		 *
844
		 * @param array $field_types The types of fields which should get the 'table-layout' class
845
		 */
846
		$repeat_table_rows_types = apply_filters( 'cmb2_repeat_table_row_types', array(
847
			'text_url', 'text',
848
		) );
849
850
		$conditional_classes = array(
851
			'cmb-type-' . str_replace( '_', '-', sanitize_html_class( $this->type() ) ) => true,
852
			'cmb2-id-' . str_replace( '_', '-', sanitize_html_class( $this->id() ) )    => true,
853
			'cmb-repeat'             => $this->args( 'repeatable' ),
854
			'cmb-repeat-group-field' => $this->group,
855
			'cmb-inline'             => $this->args( 'inline' ),
856
			'table-layout'           => 'edit' === $this->render_context && in_array( $this->type(), $repeat_table_rows_types ),
857
		);
858
859
		foreach ( $conditional_classes as $class => $condition ) {
860
			if ( $condition ) {
861
				$classes[] = $class;
862
			}
863
		}
864
865
		if ( $added_classes = $this->args( 'classes' ) ) {
866
			$added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes;
867
		} elseif ( $added_classes = $this->get_param_callback_result( 'classes_cb' ) ) {
868
			$added_classes = is_array( $added_classes ) ? implode( ' ', $added_classes ) : (string) $added_classes;
869
		}
870
871
		if ( $added_classes ) {
872
			$classes[] = esc_attr( $added_classes );
873
		}
874
875
		/**
876
		 * Globally filter row classes
877
		 *
878
		 * @since 2.0.0
879
		 *
880
		 * @param string            $classes Space-separated list of row classes
881
		 * @param CMB2_Field object $field   This field object
882
		 */
883
		return apply_filters( 'cmb2_row_classes', implode( ' ', $classes ), $this );
884
	}
885
886
887
888
	/**
889
	 * Get field display callback and render the display value in the column.
890
	 * @since 2.2.2
891
	 */
892
	public function render_column() {
893
		$this->render_context = 'display';
894
895
		$this->peform_param_callback( 'display_cb' );
896
897
		// For chaining
898
		return $this;
899
	}
900
901
	/**
902
	 * Default callback to outputs field value in a display format.
903
	 * @since 2.2.2
904
	 */
905
	public function display_value_callback() {
906
		// If field is requesting to be conditionally shown
907
		if ( ! $this->should_show() ) {
908
			return;
909
		}
910
911
		$display = new CMB2_Field_Display( $this );
912
913
		/**
914
		 * A filter to bypass the default display.
915
		 *
916
		 * The dynamic portion of the hook name, $this->type(), refers to the field type.
917
		 *
918
		 * Passing a non-null value to the filter will short-circuit the default display.
919
		 *
920
		 * @param bool|mixed         $pre_output Default null value.
921
		 * @param CMB2_Field         $field      This field object.
922
		 * @param CMB2_Field_Display $display    The `CMB2_Field_Display` object.
923
		 */
924
		$pre_output = apply_filters( "cmb2_pre_field_display_{$this->type()}", null, $this, $display );
925
926
		if ( null !== $pre_output ) {
927
			echo $pre_output;
928
			return;
929
		}
930
931
		$this->peform_param_callback( 'before_display_wrap' );
932
933
		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...
934
935
		$this->peform_param_callback( 'before_display' );
936
937
		CMB2_Field_Display::get( $this )->display();
938
939
		$this->peform_param_callback( 'after_display' );
940
941
		echo "\n</div>";
942
943
		$this->peform_param_callback( 'after_display_wrap' );
944
945
		// For chaining
946
		return $this;
947
	}
948
949
	/**
950
	 * Replaces a hash key - {#} - with the repeatable index
951
	 * @since  1.2.0
952
	 * @param  string $value Value to update
953
	 * @return string        Updated value
954
	 */
955
	public function replace_hash( $value ) {
956
		// Replace hash with 1 based count
957
		return str_replace( '{#}', ( $this->index + 1 ), $value );
958
	}
959
960
	/**
961
	 * Retrieve text parameter from field's text array (if it has one), or use fallback text
962
	 * For back-compatibility, falls back to checking the options array.
963
	 *
964
	 * @since  2.2.2
965
	 * @param  string  $text_key Key in field's text array
966
	 * @param  string  $fallback Fallback text
967
	 * @return string            Text
968
	 */
969
	public function get_string( $text_key, $fallback ) {
970
		// If null, populate with our field strings values.
971
		if ( null === $this->strings ) {
972
			$this->strings = (array) $this->args['text'];
973
974 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...
975
				$strings = call_user_func( $this->args['text_cb'], $this );
976
977
				if ( $strings && is_array( $strings ) ) {
978
					$this->strings += $strings;
979
				}
980
			}
981
		}
982
983
		// If we have that string value, send it back.
984
		if ( isset( $this->strings[ $text_key ] ) ) {
985
			return $this->strings[ $text_key ];
986
		}
987
988
		// Check options for back-compat.
989
		$string = $this->options( $text_key );
990
991
		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 991 which is incompatible with the return type documented by CMB2_Field::get_string of type string.
Loading history...
992
	}
993
994
	/**
995
	 * Retrieve options args. Calls options_cb if it exists.
996
	 * @since  2.0.0
997
	 * @param  string  $key Specific option to retrieve
998
	 * @return array        Array of options
999
	 */
1000
	public function options( $key = '' ) {
1001
		if ( ! empty( $this->field_options ) ) {
1002
			if ( $key ) {
1003
				return array_key_exists( $key, $this->field_options ) ? $this->field_options[ $key ] : false;
1004
			}
1005
1006
			return $this->field_options;
1007
		}
1008
1009
		$this->field_options = (array) $this->args['options'];
1010
1011 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...
1012
			$options = call_user_func( $this->args['options_cb'], $this );
1013
1014
			if ( $options && is_array( $options ) ) {
1015
				$this->field_options = $options + $this->field_options;
1016
			}
1017
		}
1018
1019
		if ( $key ) {
1020
			return array_key_exists( $key, $this->field_options ) ? $this->field_options[ $key ] : false;
1021
		}
1022
1023
		return $this->field_options;
1024
	}
1025
1026
	/**
1027
	 * Store JS dependencies as part of the field args.
1028
	 * @since 2.2.0
1029
	 * @param array $dependencies Dependies to register for this field.
1030
	 */
1031
	public function add_js_dependencies( $dependencies = array() ) {
1032
		foreach ( (array) $dependencies as $dependency ) {
1033
			$this->args['js_dependencies'][ $dependency ] = $dependency;
1034
		}
1035
1036
		CMB2_JS::add_dependencies( $dependencies );
1037
	}
1038
1039
	/**
1040
	 * Get CMB2_Field default value, either from default param or default_cb param.
1041
	 *
1042
	 * @since  0.2.2
1043
	 *
1044
	 * @return mixed  Default field value
1045
	 */
1046
	public function get_default() {
1047
		if ( null !== $this->args['default'] ) {
1048
			return $this->args['default'];
1049
		}
1050
1051
		$param = is_callable( $this->args['default_cb'] ) ? 'default_cb' : 'default';
1052
		$default = $this->get_param_callback_result( $param );
1053
1054
		// Allow a filter override of the default value
1055
		$this->args['default'] = apply_filters( 'cmb2_default_filter', $default, $this );
1056
1057
		return $this->args['default'];
1058
	}
1059
1060
	/**
1061
	 * Fills in empty field parameters with defaults
1062
	 * @since 1.1.0
1063
	 * @param array $args Metabox field config array
1064
	 * @param array       Modified field config array.
1065
	 */
1066
	public function _set_field_defaults( $args ) {
1067
1068
		// Set up blank or default values for empty ones
1069
		$args = wp_parse_args( $args, array(
1070
			'type'              => '',
1071
			'name'              => '',
1072
			'desc'              => '',
1073
			'before'            => '',
1074
			'after'             => '',
1075
			'options'           => array(),
1076
			'options_cb'        => '',
1077
			'text'              => array(),
1078
			'text_cb'           => '',
1079
			'attributes'        => array(),
1080
			'protocols'         => null,
1081
			'default'           => null,
1082
			'default_cb'        => '',
1083
			'classes'           => null,
1084
			'classes_cb'        => '',
1085
			'select_all_button' => true,
1086
			'multiple'          => false,
1087
			'repeatable'        => isset( $args['type'] ) && 'group' == $args['type'],
1088
			'inline'            => false,
1089
			'on_front'          => true,
1090
			'show_names'        => true,
1091
			'save_field'        => true, // Will not save if false
1092
			'date_format'       => 'm\/d\/Y',
1093
			'time_format'       => 'h:i A',
1094
			'description'       => isset( $args['desc'] ) ? $args['desc'] : '',
1095
			'preview_size'      => 'file' == $args['type'] ? array( 350, 350 ) : array( 50, 50 ),
1096
			'render_row_cb'     => array( $this, 'render_field_callback' ),
1097
			'display_cb'        => array( $this, 'display_value_callback' ),
1098
			'label_cb'          => 'title' != $args['type'] ? array( $this, 'label' ) : '',
1099
			'column'            => false,
1100
			'js_dependencies'   => array(),
1101
			'show_in_rest'      => null,
1102
		) );
1103
1104
		/*
1105
		 * Deprecated usage:
1106
		 *
1107
		 * 'std' -- use 'default' (no longer works)
1108
		 * 'row_classes' -- use 'class', or 'class_cb'
1109
		 * 'default' -- as callback (use default_cb)
1110
		 */
1111
		$args = $this->convert_deprecated_params( $args );
1112
1113
		$args['repeatable'] = $args['repeatable'] && ! $this->repeatable_exception( $args['type'] );
1114
		$args['inline']     = $args['inline'] || false !== stripos( $args['type'], '_inline' );
1115
1116
		$args['options']    = 'group' == $args['type'] ? wp_parse_args( $args['options'], array(
1117
			'add_button'    => esc_html__( 'Add Group', 'cmb2' ),
1118
			'remove_button' => esc_html__( 'Remove Group', 'cmb2' ),
1119
		) ) : $args['options'];
1120
1121
		$args['_id']        = $args['id'];
1122
		$args['_name']      = $args['id'];
1123
1124
		if ( $this->group ) {
1125
1126
			$args['id']    = $this->group->args( 'id' ) . '_' . $this->group->index . '_' . $args['id'];
1127
			$args['_name'] = $this->group->args( 'id' ) . '[' . $this->group->index . '][' . $args['_name'] . ']';
1128
		}
1129
1130
		if ( 'wysiwyg' == $args['type'] ) {
1131
			$args['id'] = strtolower( str_ireplace( '-', '_', $args['id'] ) );
1132
			$args['options']['textarea_name'] = $args['_name'];
1133
		}
1134
1135
		$option_types = apply_filters( 'cmb2_all_or_nothing_types', array( 'select', 'radio', 'radio_inline', 'taxonomy_select', 'taxonomy_radio', 'taxonomy_radio_inline' ), $this );
1136
1137
		if ( in_array( $args['type'], $option_types, true ) ) {
1138
1139
			$args['show_option_none'] = isset( $args['show_option_none'] ) ? $args['show_option_none'] : null;
1140
			$args['show_option_none'] = true === $args['show_option_none'] ? esc_html__( 'None', 'cmb2' ) : $args['show_option_none'];
1141
1142
			if ( null === $args['show_option_none'] ) {
1143
				$off_by_default = in_array( $args['type'], array( 'select', 'radio', 'radio_inline' ), true );
1144
				$args['show_option_none'] = $off_by_default ? false : esc_html__( 'None', 'cmb2' );
1145
			}
1146
1147
		}
1148
1149
		$args['has_supporting_data'] = in_array(
1150
			$args['type'],
1151
			array(
1152
				// 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...
1153
				'file',
1154
				// 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...
1155
				'text_datetime_timestamp_timezone',
1156
			),
1157
			true
1158
		);
1159
1160
		return $args;
1161
	}
1162
1163
	/**
1164
	 * Get default field arguments specific to this CMB2 object.
1165
	 * @since  2.2.0
1166
	 * @param  array      $field_args  Metabox field config array.
1167
	 * @param  CMB2_Field $field_group (optional) CMB2_Field object (group parent)
1168
	 * @return array                   Array of field arguments.
1169
	 */
1170
	protected function get_default_args( $field_args, $field_group = null ) {
1171
		$args = parent::get_default_args( array(), $this->group );
1172
1173
		if ( isset( $field_args['field_args'] ) ) {
1174
			$args = wp_parse_args( $field_args, $args );
1175
		} else {
1176
			$args['field_args'] = wp_parse_args( $field_args, $this->args );
1177
		}
1178
1179
		return $args;
1180
	}
1181
1182
	/**
1183
	 * Returns a cloned version of this field object with, but with
1184
	 * modified/overridden field arguments.
1185
	 *
1186
	 * @since  2.2.2
1187
	 * @param  array  $field_args Array of field arguments, or entire array of
1188
	 *                            arguments for CMB2_Field
1189
	 *
1190
	 * @return CMB2_Field         The new CMB2_Field instance.
1191
	 */
1192
	public function get_field_clone( $field_args ) {
1193
		return $this->get_new_field( $field_args );
1194
	}
1195
1196
	/**
1197
	 * Returns the CMB2 instance this field is registered to.
1198
	 *
1199
	 * @since  2.2.2
1200
	 *
1201
	 * @return CMB2|WP_Error If new CMB2_Field is called without cmb_id arg, returns error.
1202
	 */
1203
	public function get_cmb() {
1204
		if ( ! $this->cmb_id ) {
1205
			return new WP_Error( 'no_cmb_id', esc_html__( 'Sorry, this field does not have a cmb_id specified.', 'cmb2' ) );
1206
		}
1207
1208
		return cmb2_get_metabox( $this->cmb_id, $this->object_id, $this->object_type );
1209
	}
1210
1211
	/**
1212
	 * Converts deprecated field parameters to the current/proper parameter, and throws a deprecation notice.
1213
	 * @since 2.2.3
1214
	 * @param array $args Metabox field config array.
1215
	 * @param array       Modified field config array.
1216
	 */
1217
	protected function convert_deprecated_params( $args ) {
1218
1219
		if ( isset( $args['row_classes'] ) ) {
1220
1221
			$this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_PARAM, 'row_classes', 'classes' );
1222
1223
			// row_classes param could be a callback
1224
			if ( is_callable( $args['row_classes'] ) ) {
1225
1226
				$this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'row_classes', 'classes_cb' );
1227
1228
				$args['classes_cb'] = $args['row_classes'];
1229
				$args['classes'] = null;
1230
			} else {
1231
1232
1233
				$args['classes'] = $args['row_classes'];
1234
			}
1235
1236
			unset( $args['row_classes'] );
1237
		}
1238
1239
1240
		// default param can be passed a callback as well
1241 View Code Duplication
		if ( is_callable( $args['default'] ) ) {
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...
1242
1243
			$this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'default', 'default_cb' );
1244
1245
			$args['default_cb'] = $args['default'];
1246
			$args['default'] = null;
1247
		}
1248
1249
		// options param can be passed a callback as well
1250 View Code Duplication
		if ( is_callable( $args['options'] ) ) {
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...
1251
1252
			$this->deprecated_param( __CLASS__ . '::__construct()', '2.2.3', self::DEPRECATED_CB_PARAM, 'options', 'options_cb' );
1253
1254
			$args['options_cb'] = $args['options'];
1255
			$args['options'] = array();
1256
		}
1257
1258
		return $args;
1259
	}
1260
1261
}
1262