Completed
Pull Request — trunk (#541)
by Justin
20:13 queued 05:52
created

CMB2::get_field_ids()   D

Complexity

Conditions 9
Paths 56

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 26.496
Metric Value
dl 0
loc 27
ccs 4
cts 10
cp 0.4
rs 4.9091
cc 9
eloc 15
nc 56
nop 2
crap 26.496
1
<?php
2
/**
3
 * CMB2 - The core metabox object
4
 *
5
 * @category  WordPress_Plugin
6
 * @package   CMB2
7
 * @author    WebDevStudios
8
 * @license   GPL-2.0+
9
 * @link      http://webdevstudios.com
10
 *
11
 * @property-read string $cmb_id
12
 * @property-read array $meta_box
13
 * @property-read array $updated
14
 */
15
class CMB2 {
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...
16
17
	/**
18
	 * Current field's ID
19
	 * @var   string
20
	 * @since 2.0.0
21
	 */
22
	protected $cmb_id = '';
23
24
	/**
25
	 * Metabox Config array
26
	 * @var   array
27
	 * @since 0.9.0
28
	 */
29
	protected $meta_box = array();
30
31
	/**
32
	 * Object ID for metabox meta retrieving/saving
33
	 * @var   mixed
34
	 * @since 1.0.0
35
	 */
36
	protected $object_id = 0;
37
38
	/**
39
	 * Type of object being saved. (e.g., post, user, or comment)
40
	 * @var   string
41
	 * @since 1.0.0
42
	 */
43
	protected $object_type = 'post';
44
45
	/**
46
	 * Type of object registered for metabox. (e.g., post, user, or comment)
47
	 * @var   string
48
	 * @since 1.0.0
49
	 */
50
	protected $mb_object_type = null;
51
52
	/**
53
	 * List of fields that are changed/updated on save
54
	 * @var   array
55
	 * @since 1.1.0
56
	 */
57
	protected $updated = array();
58
59
	/**
60
	 * Metabox Defaults
61
	 * @var   array
62
	 * @since 1.0.1
63
	 */
64
	protected $mb_defaults = array(
65
		'id'               => '',
66
		'title'            => '',
67
		'type'             => '',
68
		'object_types'     => array(), // Post type
69
		'context'          => 'normal',
70
		'priority'         => 'high',
71
		'show_names'       => true, // Show field names on the left
72
		'show_on_cb'       => null, // Callback to determine if metabox should display.
73
		'show_on'          => array(), // Post IDs or page templates to display this metabox. overrides 'show_on_cb'
74
		'cmb_styles'       => true, // Include CMB2 stylesheet
75
		'enqueue_js'       => true, // Include CMB2 JS
76
		'fields'           => array(),
77
		'hookup'           => true,
78
		'save_fields'      => true, // Will not save during hookup if false
79
		'closed'           => false, // Default to metabox being closed?
80
		'taxonomies'       => array(),
81
		'new_user_section' => 'add-new-user', // or 'add-existing-user'
82
		'new_term_section' => true,
83
		'show_in_rest'     => false,
84
		'rest_read'        => false,
85
		'rest_write'       => false,
86
	);
87
88
	/**
89
	 * Metabox field objects
90
	 * @var   array
91
	 * @since 2.0.3
92
	 */
93
	protected $fields = array();
94
95
	/**
96
	 * An array of hidden fields to output at the end of the form
97
	 * @var   array
98
	 * @since 2.0.0
99
	 */
100
	protected $hidden_fields = array();
101
102
	/**
103
	 * Array of key => value data for saving. Likely $_POST data.
104
	 * @var   array
105
	 * @since 2.0.0
106
	 */
107
	public $data_to_save = array();
108
109
	/**
110
	 * Array of key => value data for saving. Likely $_POST data.
111
	 * @var   string
112
	 * @since 2.0.0
113
	 */
114
	protected $generated_nonce = '';
115
116
	/**
117
	 * Get started
118
	 * @since 0.4.0
119 42
	 * @param array   $meta_box  Metabox config array
120
	 * @param integer $object_id Optional object id
121 42
	 */
122 1
	public function __construct( $meta_box, $object_id = 0 ) {
123
124
		if ( empty( $meta_box['id'] ) ) {
125 42
			wp_die( __( 'Metabox configuration is required to have an ID parameter', 'cmb2' ) );
126 42
		}
127
128 42
		$this->meta_box = wp_parse_args( $meta_box, $this->mb_defaults );
129 42
		$this->meta_box['fields'] = array();
130 42
131
		$this->object_id( $object_id );
132 42
		$this->mb_object_type();
133 40
		$this->cmb_id = $meta_box['id'];
134 40
135
		if ( ! empty( $meta_box['fields'] ) && is_array( $meta_box['fields'] ) ) {
136 42
			$this->add_fields( $meta_box['fields'] );
137
		}
138
139
		CMB2_Boxes::add( $this );
140
141
		/**
142
		 * Hook during initiation of CMB2 object
143
		 *
144
		 * The dynamic portion of the hook name, $this->cmb_id, is this meta_box id.
145 42
		 *
146 42
		 * @param array $cmb This CMB2 object
147
		 */
148
		do_action( "cmb2_init_{$this->cmb_id}", $this );
149
	}
150
151
	/**
152
	 * Loops through and displays fields
153
	 * @since 1.0.0
154 1
	 * @param int    $object_id   Object ID
155 1
	 * @param string $object_type Type of object being saved. (e.g., post, user, or comment)
156
	 */
157 1
	public function show_form( $object_id = 0, $object_type = '' ) {
158 1
		$this->render_form_open( $object_id, $object_type );
159 1
160
		foreach ( $this->prop( 'fields' ) as $field_args ) {
161 1
			$this->render_field( $field_args );
162 1
		}
163
164
		$this->render_form_close( $object_id, $object_type );
165
	}
166
167
	/**
168
	 * Outputs the opening form markup and runs corresponding hooks:
169
	 * 'cmb2_before_form' and "cmb2_before_{$object_type}_form_{$this->cmb_id}"
170
	 * @since  2.2.0
171
	 * @param  integer $object_id   Object ID
172 1
	 * @param  string  $object_type Object type
173 1
	 * @return void
174 1
	 */
175 View Code Duplication
	public function render_form_open( $object_id = 0, $object_type = '' ) {
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...
176 1
		$object_type = $this->object_type( $object_type );
177
		$object_id = $this->object_id( $object_id );
178 1
179
		echo "\n<!-- Begin CMB2 Fields -->\n";
180
181
		$this->nonce_field();
182
183
		/**
184
		 * Hook before form table begins
185
		 *
186
		 * @param array  $cmb_id      The current box ID
187
		 * @param int    $object_id   The ID of the current object
188
		 * @param string $object_type The type of object you are working with.
189
		 *	                           Usually `post` (this applies to all post-types).
190 1
		 *	                           Could also be `comment`, `user` or `options-page`.
191
		 * @param array  $cmb         This CMB2 object
192
		 */
193
		do_action( 'cmb2_before_form', $this->cmb_id, $object_id, $object_type, $this );
194
195
		/**
196
		 * Hook before form table begins
197
		 *
198
		 * The first dynamic portion of the hook name, $object_type, is the type of object
199
		 * you are working with. Usually `post` (this applies to all post-types).
200
		 * Could also be `comment`, `user` or `options-page`.
201
		 *
202
		 * The second dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
203
		 *
204
		 * @param array  $cmb_id      The current box ID
205 1
		 * @param int    $object_id   The ID of the current object
206
		 * @param array  $cmb         This CMB2 object
207 1
		 */
208
		do_action( "cmb2_before_{$object_type}_form_{$this->cmb_id}", $object_id, $this );
209 1
210
		echo '<div class="cmb2-wrap form-table"><div id="cmb2-metabox-', sanitize_html_class( $this->cmb_id ), '" class="cmb2-metabox cmb-field-list">';
211
212
	}
213
214
	/**
215
	 * Outputs the closing form markup and runs corresponding hooks:
216
	 * 'cmb2_after_form' and "cmb2_after_{$object_type}_form_{$this->cmb_id}"
217
	 * @since  2.2.0
218
	 * @param  integer $object_id   Object ID
219 1
	 * @param  string  $object_type Object type
220 1
	 * @return void
221 1
	 */
222 View Code Duplication
	public function render_form_close( $object_id = 0, $object_type = '' ) {
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...
223 1
		$object_type = $this->object_type( $object_type );
224
		$object_id = $this->object_id( $object_id );
225 1
226
		echo '</div></div>';
227
228
		$this->render_hidden_fields();
229
230
		/**
231
		 * Hook after form form has been rendered
232
		 *
233
		 * @param array  $cmb_id      The current box ID
234
		 * @param int    $object_id   The ID of the current object
235
		 * @param string $object_type The type of object you are working with.
236
		 *	                           Usually `post` (this applies to all post-types).
237 1
		 *	                           Could also be `comment`, `user` or `options-page`.
238
		 * @param array  $cmb         This CMB2 object
239
		 */
240
		do_action( 'cmb2_after_form', $this->cmb_id, $object_id, $object_type, $this );
241
242
		/**
243
		 * Hook after form form has been rendered
244
		 *
245
		 * The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
246
		 *
247
		 * The first dynamic portion of the hook name, $object_type, is the type of object
248
		 * you are working with. Usually `post` (this applies to all post-types).
249
		 * Could also be `comment`, `user` or `options-page`.
250
		 *
251 1
		 * @param int    $object_id   The ID of the current object
252
		 * @param array  $cmb         This CMB2 object
253 1
		 */
254
		do_action( "cmb2_after_{$object_type}_form_{$this->cmb_id}", $object_id, $this );
255 1
256
		echo "\n<!-- End CMB2 Fields -->\n";
257
258
	}
259
260
	/**
261
	 * Renders a field based on the field type
262
	 * @since  2.2.0
263 1
	 * @param  array $field_args A field configuration array.
264 1
	 * @return mixed CMB2_Field object if successful.
265
	 */
266 1
	public function render_field( $field_args ) {
267
		$field_args['context'] = $this->prop( 'context' );
268
269
		if ( 'group' == $field_args['type'] ) {
270
271
			if ( ! isset( $field_args['show_names'] ) ) {
272
				$field_args['show_names'] = $this->prop( 'show_names' );
273 1
			}
274
			$field = $this->render_group( $field_args );
275
276
		} elseif ( 'hidden' == $field_args['type'] && $this->get_field( $field_args )->should_show() ) {
277
			// Save rendering for after the metabox
278
			$field = $this->add_hidden_field( array(
279
				'field_args'  => $field_args,
280
				'object_type' => $this->object_type(),
281
				'object_id'   => $this->object_id(),
282
			) );
283 1
284
		} else {
285
286 1
			$field_args['show_names'] = $this->prop( 'show_names' );
287
288
			// Render default fields
289 1
			$field = $this->get_field( $field_args )->render_field();
290
		}
291
292
		return $field;
293
	}
294
295
	/**
296
	 * Render a repeatable group.
297 2
	 * @param array $args Array of field arguments for a group field parent.
298
	 * @return CMB2_Field|null Group field object.
299 2
	 */
300
	public function render_group( $args ) {
301
302
		if ( ! isset( $args['id'], $args['fields'] ) || ! is_array( $args['fields'] ) ) {
303 2
			return;
304
		}
305
306 2
		$field_group = $this->get_field( $args );
307
308
		// If field is requesting to be conditionally shown
309
		if ( ! $field_group || ! $field_group->should_show() ) {
310 2
			return;
311 2
		}
312 2
313 2
		$desc            = $field_group->args( 'description' );
314 2
		$label           = $field_group->args( 'name' );
315 2
		$sortable        = $field_group->options( 'sortable' ) ? ' sortable' : ' non-sortable';
316 2
		$repeat_class    = $field_group->args( 'repeatable' ) ? ' repeatable' : ' non-repeatable';
317 2
		$group_val       = (array) $field_group->value();
318
		$nrows           = count( $group_val );
319 2
		$remove_disabled = $nrows <= 1 ? 'disabled="disabled" ' : '';
320
		$field_group->index = 0;
321 2
322
		$field_group->peform_param_callback( 'before_group' );
323 2
324 2
		echo '<div class="cmb-row cmb-repeat-group-wrap" data-fieldtype="group"><div class="cmb-td"><div id="', $field_group->id(), '_repeat" class="cmb-nested cmb-field-list cmb-repeatable-group', $sortable, $repeat_class, '" style="width:100%;">';
325 2
326 2
		if ( $desc || $label ) {
327 2
			$class = $desc ? ' cmb-group-description' : '';
328 2
			echo '<div class="cmb-row', $class, '"><div class="cmb-th">';
329 2
				if ( $label ) {
330 1
					echo '<h2 class="cmb-group-name">', $label, '</h2>';
331 1
				}
332 2
				if ( $desc ) {
333 2
					echo '<p class="cmb2-metabox-description">', $desc, '</p>';
334
				}
335 2
			echo '</div></div>';
336
		}
337
338
		if ( ! empty( $group_val ) ) {
339
340
			foreach ( $group_val as $group_key => $field_id ) {
341
				$this->render_group_row( $field_group, $remove_disabled );
342 2
				$field_group->index++;
343
			}
344
		} else {
345 2
			$this->render_group_row( $field_group, $remove_disabled );
346 1
		}
347 1
348
		if ( $field_group->args( 'repeatable' ) ) {
349 2
			echo '<div class="cmb-row"><div class="cmb-td"><p class="cmb-add-row"><button data-selector="', $field_group->id(), '_repeat" data-grouptitle="', $field_group->options( 'group_title' ), '" class="cmb-add-group-row button">', $field_group->options( 'add_button' ), '</button></p></div></div>';
350
		}
351 2
352
		echo '</div></div></div>';
353 2
354
		$field_group->peform_param_callback( 'after_group' );
355
356
		return $field_group;
357
	}
358
359
	/**
360
	 * Render a repeatable group row
361
	 * @since  1.0.2
362 2
	 * @param  CMB2_Field $field_group  CMB2_Field group field object
363
	 * @param  string  $remove_disabled Attribute string to disable the remove button
364 2
	 */
365 2
	public function render_group_row( $field_group, $remove_disabled ) {
366
367
		$field_group->peform_param_callback( 'before_group_row' );
368 2
		$closed_class = $field_group->options( 'closed' ) ? ' closed' : '';
369
370 2
		echo '
371 1
		<div class="postbox cmb-row cmb-repeatable-grouping', $closed_class, '" data-iterator="', $field_group->index, '">';
372 1
373
			if ( $field_group->args( 'repeatable' ) ) {
374
				echo '<button ', $remove_disabled, 'data-selector="', $field_group->id(), '_repeat" class="dashicons-before dashicons-no-alt cmb-remove-group-row"></button>';
375 2
			}
376 2
377
			echo '
378
			<div class="cmbhandle" title="' , __( 'Click to toggle', 'cmb2' ), '"><br></div>
379
			<h3 class="cmb-group-title cmbhandle-title"><span>', $field_group->replace_hash( $field_group->options( 'group_title' ) ), '</span></h3>
0 ignored issues
show
Documentation introduced by
$field_group->options('group_title') is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
380 2
381 2
			<div class="inside cmb-td cmb-nested cmb-field-list">';
382
				// Loop and render repeatable group fields
383
				foreach ( array_values( $field_group->args( 'fields' ) ) as $field_args ) {
384
					if ( 'hidden' == $field_args['type'] ) {
385
386
						// Save rendering for after the metabox
387
						$this->add_hidden_field( array(
388
							'field_args'  => $field_args,
389
							'group_field' => $field_group,
390
						) );
391 2
392 2
					} else {
393
394 2
						$field_args['show_names'] = $field_group->args( 'show_names' );
395
						$field_args['context']    = $field_group->args( 'context' );
396 2
397 2
						$field = $this->get_field( $field_args, $field_group )->render_field();
0 ignored issues
show
Unused Code introduced by
$field is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
398
					}
399
				}
400
				if ( $field_group->args( 'repeatable' ) ) {
401 1
					echo '
402
					<div class="cmb-row cmb-remove-field-row">
403
						<div class="cmb-remove-row">
404
							<button ', $remove_disabled, 'data-selector="', $field_group->id(), '_repeat" class="button cmb-remove-group-row alignright">', $field_group->options( 'remove_button' ), '</button>
405 1
						</div>
406
					</div>
407
					';
408
				}
409 2
			echo '
410
			</div>
411 2
		</div>
412 2
		';
413
414
		$field_group->peform_param_callback( 'after_group_row' );
415
	}
416
417
	/**
418
	 * Add a hidden field to the list of hidden fields to be rendered later
419
	 * @since 2.0.0
420
	 * @param array  $args Array of arguments to be passed to CMB2_Field
421
	 */
422
	public function add_hidden_field( $args ) {
423
		$field = new CMB2_Field( $args );
424
		$this->hidden_fields[] = new CMB2_Types( $field );
425
426
		return $field;
427
	}
428
429
	/**
430 1
	 * Loop through and output hidden fields
431 1
	 * @since  2.0.0
432
	 */
433
	public function render_hidden_fields() {
434
		if ( ! empty( $this->hidden_fields ) ) {
435
			foreach ( $this->hidden_fields as $hidden ) {
436 1
				$hidden->render();
437
			}
438
		}
439
	}
440
441
	/**
442
	 * Returns array of sanitized field values (without saving them)
443 2
	 * @since  2.0.3
444 2
	 * @param  array  $data_to_sanitize Array of field_id => value data for sanitizing (likely $_POST data).
445 2
	 */
446
	public function get_sanitized_values( array $data_to_sanitize ) {
447
		$this->data_to_save = $data_to_sanitize;
448 2
		$stored_id          = $this->object_id();
449
450
		// We do this So CMB will sanitize our data for us, but not save it
451 2
		$this->object_id( '_' );
452
453
		// Ensure temp. data store is empty
454 2
		cmb2_options( 0 )->set();
455
456
		// Process/save fields
457 2
		$this->process_fields();
458
459
		// Get data from temp. data store
460 2
		$sanitized_values = cmb2_options( 0 )->get_options();
461
462
		// Empty out temp. data store again
463 2
		cmb2_options( 0 )->set();
464
465 2
		// Reset the object id
466
		$this->object_id( $stored_id );
467
468
		return $sanitized_values;
469
	}
470
471
	/**
472
	 * Loops through and saves field data
473
	 * @since  1.0.0
474
	 * @param  int    $object_id    Object ID
475
	 * @param  string $object_type  Type of object being saved. (e.g., post, user, or comment)
476
	 * @param  array  $data_to_save Array of key => value data for saving. Likely $_POST data.
477
	 */
478
	public function save_fields( $object_id = 0, $object_type = '', $data_to_save = array() ) {
0 ignored issues
show
Coding Style introduced by
save_fields uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
479
480
		// Fall-back to $_POST data
481
		$this->data_to_save = ! empty( $data_to_save ) ? $data_to_save : $_POST;
482
		$object_id = $this->object_id( $object_id );
483
		$object_type = $this->object_type( $object_type );
484
485
		$this->process_fields();
486
487
		// If options page, save the updated options
488
		if ( 'options-page' == $object_type ) {
489
			cmb2_options( $object_id )->set();
490
		}
491
492
		$this->after_save();
493
	}
494
495
	/**
496
	 * Process and save form fields
497
	 * @since  2.0.0
498
	 */
499
	public function process_fields() {
500
501
		$this->pre_process();
502
503
		// Remove the show_on properties so saving works
504
		$this->prop( 'show_on', array() );
505
506
		// save field ids of those that are updated
507
		$this->updated = array();
508
509
		foreach ( $this->prop( 'fields' ) as $field_args ) {
510
			$this->process_field( $field_args );
511
		}
512
	}
513
514
	/**
515
	 * Process and save a field
516
	 * @since  2.0.0
517
	 * @param  array  $field_args Array of field arguments
518
	 */
519
	public function process_field( $field_args ) {
520
521
		switch ( $field_args['type'] ) {
522
523
			case 'group':
524
				if ( $this->save_group( $field_args ) ) {
525
					$this->updated[] = $field_args['id'];
526 2
				}
527
528
				break;
529
530
			case 'title':
531
				// Don't process title fields
532
				break;
533
534
			default:
535
536
				// Save default fields
537
				$field = new CMB2_Field( array(
538
					'field_args'  => $field_args,
539
					'object_type' => $this->object_type(),
540 2
					'object_id'   => $this->object_id(),
541
				) );
542
543 2
				if ( $field->save_field_from_data( $this->data_to_save ) ) {
544
					$this->updated[] = $field->id();
545
				}
546 2
547
				break;
548 2
		}
549 2
550 2
	}
551 2
552
	public function pre_process() {
553
		/**
554
		 * Fires before fields have been processed/saved.
555
		 *
556
		 * The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
557
		 *
558 2
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
559
		 * 	Usually `post` (this applies to all post-types).
560 2
		 *  	Could also be `comment`, `user` or `options-page`.
561
		 *
562 2
		 * @param array $cmb       This CMB2 object
563 1
		 * @param int   $object_id The ID of the current object
564 1
		 */
565
		do_action( "cmb2_{$this->object_type()}_process_fields_{$this->cmb_id}", $this, $this->object_id() );
566 1
	}
567
568
	public function after_save() {
569
		$object_type = $this->object_type();
570 1
		$object_id   = $this->object_id();
571
572
		/**
573 1
		 * Fires after all fields have been saved.
574 1
		 *
575 1
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
576 1
		 * 	Usually `post` (this applies to all post-types).
577 1
		 *  	Could also be `comment`, `user` or `options-page`.
578
		 *
579 1
		 * @param int    $object_id   The ID of the current object
580 1
		 * @param array  $cmb_id      The current box ID
581 1
		 * @param string $updated     Array of field ids that were updated.
582
		 *                            Will only include field ids that had values change.
583 1
		 * @param array  $cmb         This CMB2 object
584 2
		 */
585
		do_action( "cmb2_save_{$object_type}_fields", $object_id, $this->cmb_id, $this->updated, $this );
586 2
587
		/**
588
		 * Fires after all fields have been saved.
589
		 *
590
		 * The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
591 1
		 *
592
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
593 1
		 * 	Usually `post` (this applies to all post-types).
594
		 *  	Could also be `comment`, `user` or `options-page`.
595
		 *
596
		 * @param int    $object_id   The ID of the current object
597 1
		 * @param string $updated     Array of field ids that were updated.
598 1
		 *                            Will only include field ids that had values change.
599 1
		 * @param array  $cmb         This CMB2 object
600 1
		 */
601 1
		do_action( "cmb2_save_{$object_type}_fields_{$this->cmb_id}", $object_id, $this->updated, $this );
602 1
	}
603 1
604
	/**
605 1
	 * Save a repeatable group
606 1
	 * @since  1.x.x
607 1
	 * @param  array  $args Field arguments array
608 1
	 * @return mixed        Return of CMB2_Field::update_data()
609
	 */
610 1
	public function save_group( $args ) {
611 1
		if ( ! isset( $args['id'], $args['fields'] ) || ! is_array( $args['fields'] ) ) {
612 1
			return;
613 1
		}
614 1
615 1
		return $this->save_group_field( new CMB2_Field( array(
0 ignored issues
show
Documentation introduced by
new \CMB2_Field(array('f...=> $this->object_id())) is of type object<CMB2_Field>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
616
			'field_args'  => $args,
617 1
			'object_type' => $this->object_type(),
618
			'object_id'   => $this->object_id(),
619
		) ) );
620 1
	}
621 1
622 1
	/**
623
	 * Save a repeatable group
624
	 * @since  1.x.x
625 1
	 * @param  array $field_group CMB2_Field group field object
626
	 * @return mixed              Return of CMB2_Field::update_data()
627 1
	 */
628 1
	public function save_group_field( $field_group ) {
629 1
		$base_id = $field_group->id();
0 ignored issues
show
Bug introduced by
The method id cannot be called on $field_group (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
630 1
631
		if ( ! isset( $this->data_to_save[ $base_id ] ) ) {
632 1
			return;
633
		}
634 1
635 1
		$old        = $field_group->get_data();
0 ignored issues
show
Bug introduced by
The method get_data cannot be called on $field_group (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
636 1
		// Check if group field has sanitization_cb
637 1
		$group_vals = $field_group->sanitization_cb( $this->data_to_save[ $base_id ] );
0 ignored issues
show
Bug introduced by
The method sanitization_cb cannot be called on $field_group (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
638
		$saved      = array();
639 1
640
		$field_group->index = 0;
641 1
		$field_group->data_to_save = $this->data_to_save;
642
643 1
		foreach ( array_values( $field_group->fields() ) as $field_args ) {
0 ignored issues
show
Bug introduced by
The method fields cannot be called on $field_group (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
644
			$field = new CMB2_Field( array(
645
				'field_args'  => $field_args,
646 1
				'group_field' => $field_group,
647 1
			) );
648 1
			$sub_id = $field->id( true );
649
650 1
			foreach ( (array) $group_vals as $field_group->index => $post_vals ) {
651 1
652
				// Get value
653 1
				$new_val = isset( $group_vals[ $field_group->index ][ $sub_id ] )
654 1
					? $group_vals[ $field_group->index ][ $sub_id ]
655 1
					: false;
656
657
				// Sanitize
658 1
				$new_val = $field->sanitization_cb( $new_val );
659
660 1
				if ( is_array( $new_val ) && $field->args( 'has_supporting_data' ) ) {
661 1
					if ( $field->args( 'repeatable' ) ) {
662 1
						$_new_val = array();
663 1
						foreach ( $new_val as $group_index => $grouped_data ) {
664
							// Add the supporting data to the $saved array stack
665 1
							$saved[ $field_group->index ][ $grouped_data['supporting_field_id'] ][] = $grouped_data['supporting_field_value'];
666 1
							// Reset var to the actual value
667
							$_new_val[ $group_index ] = $grouped_data['value'];
668
						}
669
						$new_val = $_new_val;
670
					} else {
671
						// Add the supporting data to the $saved array stack
672
						$saved[ $field_group->index ][ $new_val['supporting_field_id'] ] = $new_val['supporting_field_value'];
673
						// Reset var to the actual value
674 42
						$new_val = $new_val['value'];
675 42
					}
676
				}
677 42
678 13
				// Get old value
679 13
				$old_val = is_array( $old ) && isset( $old[ $field_group->index ][ $sub_id ] )
680
					? $old[ $field_group->index ][ $sub_id ]
681
					: false;
682 42
683 11
				$is_updated = ( ! empty( $new_val ) && $new_val != $old_val );
684
				$is_removed = ( empty( $new_val ) && ! empty( $old_val ) );
685
				// Compare values and add to `$updated` array
686
				if ( $is_updated || $is_removed ) {
687 41
					$this->updated[] = $base_id . '::' . $field_group->index . '::' . $sub_id;
688 41
				}
689
690
				// Add to `$saved` array
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
691
				$saved[ $field_group->index ][ $sub_id ] = $new_val;
692
693 41
			}
694
			$saved[ $field_group->index ] = array_filter( $saved[ $field_group->index ] );
695
		}
696
		$saved = array_filter( $saved );
697
698 41
		return $field_group->update_data( $saved, true );
0 ignored issues
show
Bug introduced by
The method update_data cannot be called on $field_group (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
699
	}
700
701
	/**
702 41
	 * Get object id from global space if no id is provided
703 41
	 * @since  1.0.0
704 41
	 * @param  integer $object_id Object ID
705 41
	 * @return integer $object_id Object ID
706 41
	 */
707
	public function object_id( $object_id = 0 ) {
0 ignored issues
show
Coding Style introduced by
object_id uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
object_id uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
708
		global $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
709 41
710
		if ( $object_id ) {
711 41
			$this->object_id = $object_id;
712
			return $this->object_id;
713
		}
714
715
		if ( $this->object_id ) {
716
			return $this->object_id;
717
		}
718
719 42
		// Try to get our object ID from the global space
720
		switch ( $this->object_type() ) {
721 42
			case 'user':
722 12
				$object_id = isset( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : $object_id;
723
				$object_id = ! $object_id && 'user-new.php' != $pagenow && isset( $GLOBALS['user_ID'] ) ? $GLOBALS['user_ID'] : $object_id;
724
				break;
725 42
726 35
			case 'comment':
727 35
				$object_id = isset( $_REQUEST['c'] ) ? $_REQUEST['c'] : $object_id;
728
				$object_id = ! $object_id && isset( $GLOBALS['comments']->comment_ID ) ? $GLOBALS['comments']->comment_ID : $object_id;
729
				break;
730 42
731 40
			case 'term':
732 40
				$object_id = isset( $_REQUEST['tag_ID'] ) ? $_REQUEST['tag_ID'] : $object_id;
733
				break;
734
735 3
			default:
736
				$object_id = isset( $GLOBALS['post']->ID ) ? $GLOBALS['post']->ID : $object_id;
737 3
				$object_id = isset( $_REQUEST['post'] ) ? $_REQUEST['post'] : $object_id;
738
				break;
739
		}
740
741 3
		// reset to id or 0
742 3
		$this->object_id = $object_id ? $object_id : 0;
743 3
744 3
		return $this->object_id;
745 3
	}
746 3
747
	/**
748 3
	 * Sets the $object_type based on metabox settings
749
	 * @since  1.0.0
750
	 * @return string Object type
751
	 */
752
	public function mb_object_type() {
753
754
		if ( null !== $this->mb_object_type ) {
755
			return $this->mb_object_type;
756 3
		}
757 3
758 3
		if ( $this->is_options_page_mb() ) {
759 1
			$this->mb_object_type = 'options-page';
760 1
			return $this->mb_object_type;
761
		}
762 2
763 2
		if ( ! $this->prop( 'object_types' ) ) {
764 2
			$this->mb_object_type = 'post';
765 2
			return $this->mb_object_type;
766
		}
767 3
768
		$type = false;
769
		// check if 'object_types' is a string
770
		if ( is_string( $this->prop( 'object_types' ) ) ) {
771
			$type = $this->prop( 'object_types' );
772
		}
773
		// if it's an array of one, extract it
774
		elseif ( is_array( $this->prop( 'object_types' ) ) && 1 === count( $this->prop( 'object_types' ) ) ) {
775 42
			$cpts = $this->prop( 'object_types' );
776 42
			$type = is_string( end( $cpts ) )
777
				? end( $cpts )
778
				: false;
779
		}
780
781
		if ( ! $type ) {
782
			$this->mb_object_type = 'post';
783
			return $this->mb_object_type;
784 42
		}
785 42
786 12
		// Get our object type
787 12
		switch ( $type ) {
788
789
			case 'user':
790 42
			case 'comment':
791 42
			case 'term':
792
				$this->mb_object_type = $type;
793
				break;
794
795
			default:
796
				$this->mb_object_type = 'post';
797
				break;
798
		}
799
800
		return $this->mb_object_type;
801
	}
802
803
	/**
804
	 * Determines if metabox is for an options page
805
	 * @since  1.0.1
806
	 * @return boolean True/False
807
	 */
808
	public function is_options_page_mb() {
809
		return ( isset( $this->meta_box['show_on']['key'] ) && 'options-page' === $this->meta_box['show_on']['key'] || array_key_exists( 'options-page', $this->meta_box['show_on'] ) );
810
	}
811
812
	/**
813
	 * Returns the object type
814
	 * @since  1.0.0
815
	 * @return string Object type
816
	 */
817
	public function object_type( $object_type = '' ) {
818
		if ( $object_type ) {
819 42
			$this->object_type = $object_type;
820 42
			return $this->object_type;
821 42
		}
822 1
823 1
		if ( $this->object_type ) {
824
			return $this->object_type;
825
		}
826
827
		global $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
828
829
		if ( in_array( $pagenow, array( 'user-edit.php', 'profile.php', 'user-new.php' ), true ) ) {
830
			$this->object_type = 'user';
831
832
		} elseif ( in_array( $pagenow, array( 'edit-comments.php', 'comment.php' ), true ) ) {
833
			$this->object_type = 'comment';
834
835
		} elseif ( 'edit-tags.php' == $pagenow ) {
836
			$this->object_type = 'term';
837 15
838 15
		} else {
839
			$this->object_type = 'post';
840
		}
841
842 15
		return $this->object_type;
843
	}
844 15
845 15
	/**
846
	 * Get metabox property and optionally set a fallback
847 15
	 * @since  2.0.0
848
	 * @param  string $property Metabox config property to retrieve
849
	 * @param  mixed  $fallback Fallback value to set if no value found
850
	 * @return mixed            Metabox config property value or false
851 15
	 */
852
	public function prop( $property, $fallback = null ) {
853 15
		if ( array_key_exists( $property, $this->meta_box ) ) {
854 15
			return $this->meta_box[ $property ];
855 3
		} elseif ( $fallback ) {
856
			return $this->meta_box[ $property ] = $fallback;
857
		}
858 13
	}
859
860 13
	/**
861
	 * Get a field object
862
	 *
863
	 * @since  2.0.3
864
	 *
865
	 * @param  string|array|CMB2_Field $field       Metabox field id or field config array or CMB2_Field object
866
	 * @param  CMB2_Field              $field_group (optional) CMB2_Field object (group parent)
867
	 *
868
	 * @return CMB2_Field|false CMB2_Field object (or false)
869
	 */
870
	public function get_field( $field, $field_group = null ) {
871
		if ( is_a( $field, 'CMB2_Field' ) ) {
872 13
			return $field;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $field; (string|array|CMB2_Field) is incompatible with the return type documented by CMB2::get_field of type CMB2_Field|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
873
		}
874
875 13
		$field_id = is_string( $field ) ? $field : $field['id'];
876
877
		$parent_field_id = ! empty( $field_group ) ? $field_group->id() : '';
878 2
		$ids = $this->get_field_ids( $field_id, $parent_field_id, true );
0 ignored issues
show
Unused Code introduced by
The call to CMB2::get_field_ids() has too many arguments starting with true.

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...
879
880
		if ( ! $ids ) {
881 2
			return false;
882 2
		}
883 2
884
		list( $field_id, $sub_field_id ) = $ids;
885
886
		$index = implode( '', $ids ) . ( $field_group ? $field_group->index : '' );
887 13
		if ( array_key_exists( $index, $this->fields ) ) {
888 2
			return $this->fields[ $index ];
889 2
		}
890
891
		$this->fields[ $index ] = new CMB2_Field( $this->get_field_args( $field_id, $field, $sub_field_id, $field_group ) );
892 13
893 13
		return $this->fields[ $index ];
894 13
	}
895 13
896
	/**
897
	 * Handles determining which type of arguments to pass to CMB2_Field
898
	 * @since  2.0.7
899
	 * @param  mixed  $field_id     Field (or group field) ID
900
	 * @param  mixed  $field_args   Array of field arguments
901
	 * @param  mixed  $sub_field_id Sub field ID (if field_group exists)
902
	 * @param  mixed  $field_group  If a sub-field, will be the parent group CMB2_Field object
903
	 * @return array                Array of CMB2_Field arguments
904 40
	 */
905 40
	public function get_field_args( $field_id, $field_args, $sub_field_id, $field_group ) {
906
907 40
		// Check if group is passed and if fields were added in the old-school fields array
908 40
		if ( $field_group && ( $sub_field_id || 0 === $sub_field_id ) ) {
909
910
			// Update the fields array w/ any modified properties inherited from the group field
911
			$this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ] = $field_args;
912
913
			return array(
914 40
				'field_args'  => $field_args,
915 40
				'group_field' => $field_group,
916
			);
917 40
918
		}
919
920 40
		if ( is_array( $field_args ) ) {
921 40
			$this->meta_box['fields'][ $field_id ] = array_merge( $field_args, $this->meta_box['fields'][ $field_id ] );
922
		}
923
924
		return array(
925
			'field_args'  => $this->meta_box['fields'][ $field_id ],
926
			'object_type' => $this->object_type(),
927
			'object_id'   => $this->object_id(),
928
		);
929
	}
930 42
931 42
	/**
932
	 * When fields are added in the old-school way, intitate them as they should be
933
	 * @since 2.1.0
934
	 * @param array $fields          Array of fields to add
935 42
	 * @param mixed $parent_field_id Parent field id or null
936 42
	 */
937 42
	protected function add_fields( $fields, $parent_field_id = null ) {
938
		foreach ( $fields as $field ) {
939 42
940
			$sub_fields = false;
941 42
			if ( array_key_exists( 'fields', $field ) ) {
942
				$sub_fields = $field['fields'];
943
				unset( $field['fields'] );
944
			}
945
946
			$field_id = $parent_field_id
947
				? $this->add_group_field( $parent_field_id, $field )
948
				: $this->add_field( $field );
949
950
			if ( $sub_fields ) {
951
				$this->add_fields( $sub_fields, $field_id );
952 3
			}
953 3
		}
954
	}
955
956
	/**
957 3
	 * Add a field to the metabox
958
	 * @since  2.0.0
959 3
	 * @param  array  $field           Metabox field config array
960
	 * @param  int    $position        (optional) Position of metabox. 1 for first, etc
961
	 * @return mixed                   Field id or false
962
	 */
963 3
	public function add_field( array $field, $position = 0 ) {
964 3
		if ( ! is_array( $field ) || ! array_key_exists( 'id', $field ) ) {
965 3
			return false;
966
		}
967 3
968 3
		$this->_add_field_to_array(
969 3
			$field,
970
			$this->meta_box['fields'],
971 3
			$position
972
		);
973 3
974
		return $field['id'];
975
	}
976
977
	/**
978
	 * Add a field to a group
979
	 * @since  2.0.0
980
	 * @param  string $parent_field_id The field id of the group field to add the field
981
	 * @param  array  $field           Metabox field config array
982
	 * @param  int    $position        (optional) Position of metabox. 1 for first, etc
983 42
	 * @return mixed                   Array of parent/field ids or false
984 42
	 */
985 1
	public function add_group_field( $parent_field_id, array $field, $position = 0 ) {
986 1
		if ( ! array_key_exists( $parent_field_id, $this->meta_box['fields'] ) ) {
987 42
			return false;
988
		}
989 42
990
		$parent_field = $this->meta_box['fields'][ $parent_field_id ];
991
992
		if ( 'group' !== $parent_field['type'] ) {
993
			return false;
994
		}
995
996
		if ( ! isset( $parent_field['fields'] ) ) {
997
			$this->meta_box['fields'][ $parent_field_id ]['fields'] = array();
998 2
		}
999 2
1000
		$this->_add_field_to_array(
1001 2
			$field,
1002
			$this->meta_box['fields'][ $parent_field_id ]['fields'],
1003
			$position
1004
		);
1005 2
1006
		return array( $parent_field_id, $field['id'] );
1007 2
	}
1008
1009 2
	/**
1010 1
	 * Add a field array to a fields array in desired position
1011 1
	 * @since 2.0.2
1012
	 * @param array   $field    Metabox field config array
1013
	 * @param array   &$fields  Array (passed by reference) to append the field (array) to
1014 1
	 * @param integer $position Optionally specify a position in the array to be inserted
1015 1
	 */
1016 1
	protected function _add_field_to_array( $field, &$fields, $position = 0 ) {
1017
		if ( $position ) {
1018
			cmb2_utils()->array_insert( $fields, array( $field['id'] => $field ), $position );
1019
		} else {
1020
			$fields[ $field['id'] ] = $field;
1021
		}
1022
	}
1023
1024
	/**
1025
	 * Remove a field from the metabox
1026
	 * @since 2.0.0
1027
	 * @param  string $field_id        The field id of the field to remove
1028 4
	 * @param  string $parent_field_id (optional) The field id of the group field to remove field from
1029 4
	 * @return bool                    True if field was removed
1030
	 */
1031 4
	public function remove_field( $field_id, $parent_field_id = '' ) {
1032 2
		$ids = $this->get_field_ids( $field_id, $parent_field_id );
1033
1034
		if ( ! $ids ) {
1035 2
			return false;
1036
		}
1037 2
1038 2
		list( $field_id, $sub_field_id ) = $ids;
1039 2
1040
		unset( $this->fields[ implode( '', $ids ) ] );
1041
1042
		if ( ! $sub_field_id ) {
1043
			unset( $this->meta_box['fields'][ $field_id ] );
1044
			return true;
1045
		}
1046
1047
		unset( $this->fields[ $field_id ]->args['fields'][ $sub_field_id ] );
1048
		unset( $this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ] );
1049
		return true;
1050
	}
1051
1052
	/**
1053 19
	 * Update or add a property to a field
1054 19
	 * @since  2.0.0
1055 19
	 * @param  string $field_id        Field id
1056 19
	 * @param  string $property        Field property to set/update
1057
	 * @param  mixed  $value           Value to set the field property
1058 19
	 * @param  string $parent_field_id (optional) The field id of the group field to remove field from
1059 2
	 * @return mixed                   Field id. Strict compare to false, as success can return a falsey value (like 0)
1060 2
	 */
1061
	public function update_field_property( $field_id, $property, $value, $parent_field_id = '' ) {
1062 19
		$ids = $this->get_field_ids( $field_id, $parent_field_id );
1063 2
1064
		if ( ! $ids ) {
1065
			return false;
1066 17
		}
1067 17
1068
		list( $field_id, $sub_field_id ) = $ids;
1069
1070 3
		if ( ! $sub_field_id ) {
1071
			$this->meta_box['fields'][ $field_id ][ $property ] = $value;
1072
			return $field_id;
1073
		}
1074 3
1075
		$this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ][ $property ] = $value;
1076
		return $field_id;
1077
	}
1078 3
1079
	/**
1080
	 * Check if field ids match a field and return the index/field id
1081
	 * @since  2.0.2
1082
	 * @param  string  $field_id        Field id
1083
	 * @param  string  $parent_field_id (optional) Parent field id
1084
	 * @return mixed                    Array of field/parent ids, or false
1085
	 */
1086
	public function get_field_ids( $field_id, $parent_field_id = '' ) {
1087
		$sub_field_id = $parent_field_id ? $field_id : '';
1088 2
		$field_id     = $parent_field_id ? $parent_field_id : $field_id;
1089 2
		$fields       =& $this->meta_box['fields'];
1090 2
1091 2
		if ( ! array_key_exists( $field_id, $fields ) ) {
1092
			$field_id = $this->search_old_school_array( $field_id, $fields );
1093
		}
1094
1095
		if ( false === $field_id ) {
1096
			return false;
1097
		}
1098
1099
		if ( ! $sub_field_id ) {
1100
			return array( $field_id, $sub_field_id );
1101
		}
1102
1103
		if ( 'group' !== $fields[ $field_id ]['type'] ) {
1104
			return false;
1105
		}
1106
1107
		if ( ! array_key_exists( $sub_field_id, $fields[ $field_id ]['fields'] ) ) {
1108
			$sub_field_id = $this->search_old_school_array( $sub_field_id, $fields[ $field_id ]['fields'] );
1109
		}
1110
1111
		return false === $sub_field_id ? false : array( $field_id, $sub_field_id );
1112
	}
1113
1114
	/**
1115
	 * When using the old array filter, it is unlikely field array indexes will be the field id
1116
	 * @since  2.0.2
1117
	 * @param  string $field_id The field id
1118 1
	 * @param  array  $fields   Array of fields to search
1119 1
	 * @return mixed            Field index or false
1120 1
	 */
1121
	public function search_old_school_array( $field_id, $fields ) {
1122
		$ids = wp_list_pluck( $fields, 'id' );
1123
		$index = array_search( $field_id, $ids );
1124
		return false !== $index ? $index : false;
1125
	}
1126
1127 1
	/**
1128 1
	 * Determine whether this cmb object should show, based on the 'show_on_cb' callback.
1129 1
	 *
1130
	 * @since 2.0.9
1131 1
	 *
1132 1
	 * @return bool Whether this cmb should be shown.
1133
	 */
1134 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...
1135
		// Default to showing this cmb
1136
		$show = true;
1137
1138
		// Use the callback to determine showing the cmb, if it exists
1139
		if ( is_callable( $this->prop( 'show_on_cb' ) ) ) {
1140
			$show = (bool) call_user_func( $this->prop( 'show_on_cb' ), $this );
1141 42
		}
1142
1143 42
		return $show;
1144 42
	}
1145 42
1146 42
	/**
1147 3
	 * Generate a unique nonce field for each registered meta_box
1148 1
	 * @since  2.0.0
1149 2
	 * @return string unique nonce hidden input
1150 2
	 */
1151 2
	public function nonce_field() {
1152
		wp_nonce_field( $this->nonce(), $this->nonce(), false, true );
1153
	}
1154
1155
	/**
1156
	 * Generate a unique nonce for each registered meta_box
1157
	 * @since  2.0.0
1158
	 * @return string unique nonce string
1159
	 */
1160
	public function nonce() {
1161
		if ( $this->generated_nonce ) {
1162
			return $this->generated_nonce;
1163
		}
1164
		$this->generated_nonce = sanitize_html_class( 'nonce_' . basename( __FILE__ ) . $this->cmb_id );
1165
		return $this->generated_nonce;
1166
	}
1167
1168
	/**
1169
	 * Magic getter for our object.
1170
	 * @param string $field
1171
	 * @throws Exception Throws an exception if the field is invalid.
1172
	 * @return mixed
1173
	 */
1174
	public function __get( $field ) {
1175
		switch ( $field ) {
1176
			case 'cmb_id':
1177
			case 'meta_box':
1178
			case 'updated':
1179
				return $this->{$field};
1180
			case 'object_id':
1181
				return $this->object_id();
1182
			default:
1183
				throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field );
1184
		}
1185
	}
1186
1187
}
1188