Completed
Push — trunk ( c5913a...941a5a )
by Justin
04:33
created

CMB2::render_form_close()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 37
Code Lines 8

Duplication

Lines 37
Ratio 100 %

Code Coverage

Tests 9
CRAP Score 1
Metric Value
dl 37
loc 37
ccs 9
cts 9
cp 1
rs 8.8571
cc 1
eloc 8
nc 1
nop 2
crap 1
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
	);
84
85
	/**
86
	 * Metabox field objects
87
	 * @var   array
88
	 * @since 2.0.3
89
	 */
90
	protected $fields = array();
91
92
	/**
93
	 * An array of hidden fields to output at the end of the form
94
	 * @var   array
95
	 * @since 2.0.0
96
	 */
97
	protected $hidden_fields = array();
98
99
	/**
100
	 * Array of key => value data for saving. Likely $_POST data.
101
	 * @var   array
102
	 * @since 2.0.0
103
	 */
104
	public $data_to_save = array();
105
106
	/**
107
	 * Array of key => value data for saving. Likely $_POST data.
108
	 * @var   string
109
	 * @since 2.0.0
110
	 */
111
	protected $generated_nonce = '';
112
113
	/**
114
	 * Get started
115
	 * @since 0.4.0
116
	 * @param array   $meta_box  Metabox config array
117
	 * @param integer $object_id Optional object id
118
	 */
119 43
	public function __construct( $meta_box, $object_id = 0 ) {
120
121 43
		if ( empty( $meta_box['id'] ) ) {
122 1
			wp_die( __( 'Metabox configuration is required to have an ID parameter', 'cmb2' ) );
123
		}
124
125 43
		$this->meta_box = wp_parse_args( $meta_box, $this->mb_defaults );
126 43
		$this->meta_box['fields'] = array();
127
128 43
		$this->object_id( $object_id );
129 43
		$this->mb_object_type();
130 43
		$this->cmb_id = $meta_box['id'];
131
132 43
		if ( ! empty( $meta_box['fields'] ) && is_array( $meta_box['fields'] ) ) {
133 41
			$this->add_fields( $meta_box['fields'] );
134 41
		}
135
136 43
		CMB2_Boxes::add( $this );
137
138
		/**
139
		 * Hook during initiation of CMB2 object
140
		 *
141
		 * The dynamic portion of the hook name, $this->cmb_id, is this meta_box id.
142
		 *
143
		 * @param array $cmb This CMB2 object
144
		 */
145 43
		do_action( "cmb2_init_{$this->cmb_id}", $this );
146 43
	}
147
148
	/**
149
	 * Loops through and displays fields
150
	 * @since 1.0.0
151
	 * @param int    $object_id   Object ID
152
	 * @param string $object_type Type of object being saved. (e.g., post, user, or comment)
153
	 */
154 1
	public function show_form( $object_id = 0, $object_type = '' ) {
155 1
		$this->render_form_open( $object_id, $object_type );
156
157 1
		foreach ( $this->prop( 'fields' ) as $field_args ) {
158 1
			$this->render_field( $field_args );
159 1
		}
160
161 1
		$this->render_form_close( $object_id, $object_type );
162 1
	}
163
164
	/**
165
	 * Outputs the opening form markup and runs corresponding hooks:
166
	 * 'cmb2_before_form' and "cmb2_before_{$object_type}_form_{$this->cmb_id}"
167
	 * @since  2.2.0
168
	 * @param  integer $object_id   Object ID
169
	 * @param  string  $object_type Object type
170
	 * @return void
171
	 */
172 1 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...
173 1
		$object_type = $this->object_type( $object_type );
174 1
		$object_id = $this->object_id( $object_id );
175
176 1
		$this->nonce_field();
177
178 1
		echo "\n<!-- Begin CMB2 Fields -->\n";
179
180
		/**
181
		 * Hook before form table begins
182
		 *
183
		 * @param array  $cmb_id      The current box ID
184
		 * @param int    $object_id   The ID of the current object
185
		 * @param string $object_type The type of object you are working with.
186
		 *	                           Usually `post` (this applies to all post-types).
187
		 *	                           Could also be `comment`, `user` or `options-page`.
188
		 * @param array  $cmb         This CMB2 object
189
		 */
190 1
		do_action( 'cmb2_before_form', $this->cmb_id, $object_id, $object_type, $this );
191
192
		/**
193
		 * Hook before form table begins
194
		 *
195
		 * The first dynamic portion of the hook name, $object_type, is the type of object
196
		 * you are working with. Usually `post` (this applies to all post-types).
197
		 * Could also be `comment`, `user` or `options-page`.
198
		 *
199
		 * The second dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
200
		 *
201
		 * @param array  $cmb_id      The current box ID
202
		 * @param int    $object_id   The ID of the current object
203
		 * @param array  $cmb         This CMB2 object
204
		 */
205 1
		do_action( "cmb2_before_{$object_type}_form_{$this->cmb_id}", $object_id, $this );
206
207 1
		echo '<div class="cmb2-wrap form-table"><div id="cmb2-metabox-', sanitize_html_class( $this->cmb_id ), '" class="cmb2-metabox cmb-field-list">';
208
209 1
	}
210
211
	/**
212
	 * Outputs the closing form markup and runs corresponding hooks:
213
	 * 'cmb2_after_form' and "cmb2_after_{$object_type}_form_{$this->cmb_id}"
214
	 * @since  2.2.0
215
	 * @param  integer $object_id   Object ID
216
	 * @param  string  $object_type Object type
217
	 * @return void
218
	 */
219 1 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...
220 1
		$object_type = $this->object_type( $object_type );
221 1
		$object_id = $this->object_id( $object_id );
222
223 1
		echo '</div></div>';
224
225 1
		$this->render_hidden_fields();
226
227
		/**
228
		 * Hook after form form has been rendered
229
		 *
230
		 * @param array  $cmb_id      The current box ID
231
		 * @param int    $object_id   The ID of the current object
232
		 * @param string $object_type The type of object you are working with.
233
		 *	                           Usually `post` (this applies to all post-types).
234
		 *	                           Could also be `comment`, `user` or `options-page`.
235
		 * @param array  $cmb         This CMB2 object
236
		 */
237 1
		do_action( 'cmb2_after_form', $this->cmb_id, $object_id, $object_type, $this );
238
239
		/**
240
		 * Hook after form form has been rendered
241
		 *
242
		 * The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
243
		 *
244
		 * The first dynamic portion of the hook name, $object_type, is the type of object
245
		 * you are working with. Usually `post` (this applies to all post-types).
246
		 * Could also be `comment`, `user` or `options-page`.
247
		 *
248
		 * @param int    $object_id   The ID of the current object
249
		 * @param array  $cmb         This CMB2 object
250
		 */
251 1
		do_action( "cmb2_after_{$object_type}_form_{$this->cmb_id}", $object_id, $this );
252
253 1
		echo "\n<!-- End CMB2 Fields -->\n";
254
255 1
	}
256
257
	/**
258
	 * Renders a field based on the field type
259
	 * @since  2.2.0
260
	 * @param  array $field_args A field configuration array.
261
	 * @return mixed CMB2_Field object if successful.
262
	 */
263 1
	public function render_field( $field_args ) {
264 1
		$field_args['context'] = $this->prop( 'context' );
265
266 1
		if ( 'group' == $field_args['type'] ) {
267
268
			if ( ! isset( $field_args['show_names'] ) ) {
269
				$field_args['show_names'] = $this->prop( 'show_names' );
270
			}
271
			$field = $this->render_group( $field_args );
272
273 1
		} elseif ( 'hidden' == $field_args['type'] && $this->get_field( $field_args )->should_show() ) {
274
			// Save rendering for after the metabox
275
			$field = $this->add_hidden_field( array(
276
				'field_args'  => $field_args,
277
				'object_type' => $this->object_type(),
278
				'object_id'   => $this->object_id(),
279
			) );
280
281
		} else {
282
283 1
			$field_args['show_names'] = $this->prop( 'show_names' );
284
285
			// Render default fields
286 1
			$field = $this->get_field( $field_args )->render_field();
287
		}
288
289 1
		return $field;
290
	}
291
292
	/**
293
	 * Render a repeatable group.
294
	 * @param array $args Array of field arguments for a group field parent.
295
	 * @return CMB2_Field|null Group field object.
296
	 */
297 2
	public function render_group( $args ) {
298
299 2 View Code Duplication
		if ( ! isset( $args['id'], $args['fields'] ) || ! is_array( $args['fields'] ) ) {
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...
300
			return;
301
		}
302
303 2
		$field_group = $this->get_field( $args );
304
305
		// If field is requesting to be conditionally shown
306 2
		if ( ! $field_group || ! $field_group->should_show() ) {
307
			return;
308
		}
309
310 2
		$desc            = $field_group->args( 'description' );
311 2
		$label           = $field_group->args( 'name' );
312 2
		$sortable        = $field_group->options( 'sortable' ) ? ' sortable' : ' non-sortable';
313 2
		$repeat_class    = $field_group->args( 'repeatable' ) ? ' repeatable' : ' non-repeatable';
314 2
		$group_val       = (array) $field_group->value();
315 2
		$nrows           = count( $group_val );
316 2
		$remove_disabled = $nrows <= 1 ? 'disabled="disabled" ' : '';
317 2
		$field_group->index = 0;
318
319 2
		$field_group->peform_param_callback( 'before_group' );
320
321 2
		echo '<div class="cmb-row cmb-repeat-group-wrap"><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%;">';
322
323 2
		if ( $desc || $label ) {
324 2
			$class = $desc ? ' cmb-group-description' : '';
325 2
			echo '<div class="cmb-row', $class, '"><div class="cmb-th">';
326 2
				if ( $label ) {
327 2
					echo '<h2 class="cmb-group-name">', $label, '</h2>';
328 2
				}
329 2
				if ( $desc ) {
330 1
					echo '<p class="cmb2-metabox-description">', $desc, '</p>';
331 1
				}
332 2
			echo '</div></div>';
333 2
		}
334
335 2
		if ( ! empty( $group_val ) ) {
336
337
			foreach ( $group_val as $group_key => $field_id ) {
338
				$this->render_group_row( $field_group, $remove_disabled );
339
				$field_group->index++;
340
			}
341
		} else {
342 2
			$this->render_group_row( $field_group, $remove_disabled );
343
		}
344
345 2
		if ( $field_group->args( 'repeatable' ) ) {
346 1
			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>';
347 1
		}
348
349 2
		echo '</div></div></div>';
350
351 2
		$field_group->peform_param_callback( 'after_group' );
352
353 2
		return $field_group;
354
	}
355
356
	/**
357
	 * Render a repeatable group row
358
	 * @since  1.0.2
359
	 * @param  CMB2_Field $field_group  CMB2_Field group field object
360
	 * @param  string  $remove_disabled Attribute string to disable the remove button
361
	 */
362 2
	public function render_group_row( $field_group, $remove_disabled ) {
363
364 2
		$field_group->peform_param_callback( 'before_group_row' );
365 2
		$closed_class = $field_group->options( 'closed' ) ? ' closed' : '';
366
367
		echo '
368 2
		<div class="postbox cmb-row cmb-repeatable-grouping', $closed_class, '" data-iterator="', $field_group->index, '">';
369
370 2
			if ( $field_group->args( 'repeatable' ) ) {
371 1
				echo '<button ', $remove_disabled, 'data-selector="', $field_group->id(), '_repeat" class="dashicons-before dashicons-no-alt cmb-remove-group-row"></button>';
372 1
			}
373
374
			echo '
375 2
			<div class="cmbhandle" title="' , __( 'Click to toggle', 'cmb2' ), '"><br></div>
376 2
			<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...
377
378
			<div class="inside cmb-td cmb-nested cmb-field-list">';
379
				// Loop and render repeatable group fields
380 2
				foreach ( array_values( $field_group->args( 'fields' ) ) as $field_args ) {
381 2
					if ( 'hidden' == $field_args['type'] ) {
382
383
						// Save rendering for after the metabox
384
						$this->add_hidden_field( array(
385
							'field_args'  => $field_args,
386
							'group_field' => $field_group,
387
						) );
388
389
					} else {
390
391 2
						$field_args['show_names'] = $field_group->args( 'show_names' );
392 2
						$field_args['context']    = $field_group->args( 'context' );
393
394 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...
395
					}
396 2
				}
397 2
				if ( $field_group->args( 'repeatable' ) ) {
398
					echo '
399
					<div class="cmb-row cmb-remove-field-row">
400
						<div class="cmb-remove-row">
401 1
							<button ', $remove_disabled, 'data-selector="', $field_group->id(), '_repeat" class="button cmb-remove-group-row alignright">', $field_group->options( 'remove_button' ), '</button>
402
						</div>
403
					</div>
404
					';
405 1
				}
406
			echo '
407
			</div>
408
		</div>
409 2
		';
410
411 2
		$field_group->peform_param_callback( 'after_group_row' );
412 2
	}
413
414
	/**
415
	 * Add a hidden field to the list of hidden fields to be rendered later
416
	 * @since 2.0.0
417
	 * @param array  $args Array of arguments to be passed to CMB2_Field
418
	 */
419
	public function add_hidden_field( $args ) {
420
		$field = new CMB2_Field( $args );
421
		$this->hidden_fields[] = new CMB2_Types( $field );
422
423
		return $field;
424
	}
425
426
	/**
427
	 * Loop through and output hidden fields
428
	 * @since  2.0.0
429
	 */
430 1
	public function render_hidden_fields() {
431 1
		if ( ! empty( $this->hidden_fields ) ) {
432
			foreach ( $this->hidden_fields as $hidden ) {
433
				$hidden->render();
434
			}
435
		}
436 1
	}
437
438
	/**
439
	 * Returns array of sanitized field values (without saving them)
440
	 * @since  2.0.3
441
	 * @param  array  $data_to_sanitize Array of field_id => value data for sanitizing (likely $_POST data).
442
	 */
443 2
	public function get_sanitized_values( array $data_to_sanitize ) {
444 2
		$this->data_to_save = $data_to_sanitize;
445 2
		$stored_id          = $this->object_id();
446
447
		// We do this So CMB will sanitize our data for us, but not save it
448 2
		$this->object_id( '_' );
449
450
		// Ensure temp. data store is empty
451 2
		cmb2_options( 0 )->set();
452
453
		// Process/save fields
454 2
		$this->process_fields();
455
456
		// Get data from temp. data store
457 2
		$sanitized_values = cmb2_options( 0 )->get_options();
458
459
		// Empty out temp. data store again
460 2
		cmb2_options( 0 )->set();
461
462
		// Reset the object id
463 2
		$this->object_id( $stored_id );
464
465 2
		return $sanitized_values;
466
	}
467
468
	/**
469
	 * Loops through and saves field data
470
	 * @since  1.0.0
471
	 * @param  int    $object_id    Object ID
472
	 * @param  string $object_type  Type of object being saved. (e.g., post, user, or comment)
473
	 * @param  array  $data_to_save Array of key => value data for saving. Likely $_POST data.
474
	 */
475 1
	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...
476
477
		// Fall-back to $_POST data
478 1
		$this->data_to_save = ! empty( $data_to_save ) ? $data_to_save : $_POST;
479 1
		$object_id = $this->object_id( $object_id );
480 1
		$object_type = $this->object_type( $object_type );
481
482 1
		$this->process_fields();
483
484
		// If options page, save the updated options
485 1
		if ( 'options-page' == $object_type ) {
486 1
			cmb2_options( $object_id )->set();
487 1
		}
488
489
		/**
490
		 * Fires after all fields have been saved.
491
		 *
492
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
493
		 * 	Usually `post` (this applies to all post-types).
494
		 *  	Could also be `comment`, `user` or `options-page`.
495
		 *
496
		 * @param int    $object_id   The ID of the current object
497
		 * @param array  $cmb_id      The current box ID
498
		 * @param string $updated     Array of field ids that were updated.
499
		 *                            Will only include field ids that had values change.
500
		 * @param array  $cmb         This CMB2 object
501
		 */
502 1
		do_action( "cmb2_save_{$object_type}_fields", $object_id, $this->cmb_id, $this->updated, $this );
503
504
		/**
505
		 * Fires after all fields have been saved.
506
		 *
507
		 * The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
508
		 *
509
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
510
		 * 	Usually `post` (this applies to all post-types).
511
		 *  	Could also be `comment`, `user` or `options-page`.
512
		 *
513
		 * @param int    $object_id   The ID of the current object
514
		 * @param string $updated     Array of field ids that were updated.
515
		 *                            Will only include field ids that had values change.
516
		 * @param array  $cmb         This CMB2 object
517
		 */
518 1
		do_action( "cmb2_save_{$object_type}_fields_{$this->cmb_id}", $object_id, $this->updated, $this );
519
520 1
	}
521
522
	/**
523
	 * Process and save form fields
524
	 * @since  2.0.0
525
	 */
526 3
	public function process_fields() {
527
528
		/**
529
		 * Fires before fields have been processed/saved.
530
		 *
531
		 * The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
532
		 *
533
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
534
		 * 	Usually `post` (this applies to all post-types).
535
		 *  	Could also be `comment`, `user` or `options-page`.
536
		 *
537
		 * @param array $cmb       This CMB2 object
538
		 * @param int   $object_id The ID of the current object
539
		 */
540 3
		do_action( "cmb2_{$this->object_type()}_process_fields_{$this->cmb_id}", $this, $this->object_id() );
541
542
		// Remove the show_on properties so saving works
543 3
		$this->prop( 'show_on', array() );
544
545
		// save field ids of those that are updated
546 3
		$this->updated = array();
547
548 3
		foreach ( $this->prop( 'fields' ) as $field_args ) {
549 3
			$this->process_field( $field_args );
550 3
		}
551 3
	}
552
553
	/**
554
	 * Process and save a field
555
	 * @since  2.0.0
556
	 * @param  array  $field_args Array of field arguments
557
	 */
558 3
	public function process_field( $field_args ) {
559
560 3
		switch ( $field_args['type'] ) {
561
562 3
			case 'group':
563 1
				$this->save_group( $field_args );
564 1
				break;
565
566 2
			case 'title':
567
				// Don't process title fields
568
				break;
569
570 2
			default:
0 ignored issues
show
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
571
572
				// Save default fields
573 2
				$field = new CMB2_Field( array(
574 2
					'field_args'  => $field_args,
575 2
					'object_type' => $this->object_type(),
576 2
					'object_id'   => $this->object_id(),
577 2
				) );
578
579 2
				if ( $field->save_field_from_data( $this->data_to_save ) ) {
580 2
					$this->updated[] = $field->id();
581 2
				}
582
583 2
				break;
584 3
		}
585
586 3
	}
587
588
	/**
589
	 * Save a repeatable group
590
	 */
591 1
	public function save_group( $args ) {
592
593 1 View Code Duplication
		if ( ! isset( $args['id'], $args['fields'], $this->data_to_save[ $args['id'] ] ) || ! is_array( $args['fields'] ) ) {
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...
594
			return;
595
		}
596
597 1
		$field_group        = new CMB2_Field( array(
598 1
			'field_args'  => $args,
599 1
			'object_type' => $this->object_type(),
600 1
			'object_id'   => $this->object_id(),
601 1
		) );
602 1
		$base_id            = $field_group->id();
603 1
		$old                = $field_group->get_data();
604
		// Check if group field has sanitization_cb
605 1
		$group_vals         = $field_group->sanitization_cb( $this->data_to_save[ $base_id ] );
606 1
		$saved              = array();
607 1
		$field_group->index = 0;
608 1
		$field_group->data_to_save = $this->data_to_save;
609
610 1
		foreach ( array_values( $field_group->fields() ) as $field_args ) {
611 1
			$field = new CMB2_Field( array(
612 1
				'field_args'  => $field_args,
613 1
				'group_field' => $field_group,
614 1
			) );
615 1
			$sub_id = $field->id( true );
616
617 1
			foreach ( (array) $group_vals as $field_group->index => $post_vals ) {
618
619
				// Get value
620 1
				$new_val = isset( $group_vals[ $field_group->index ][ $sub_id ] )
621 1
					? $group_vals[ $field_group->index ][ $sub_id ]
622 1
					: false;
623
624
				// Sanitize
625 1
				$new_val = $field->sanitization_cb( $new_val );
626
627 1
				if ( is_array( $new_val ) && $field->args( 'has_supporting_data' ) ) {
628 1
					if ( $field->args( 'repeatable' ) ) {
629 1
						$_new_val = array();
630 1
						foreach ( $new_val as $group_index => $grouped_data ) {
631
							// Add the supporting data to the $saved array stack
632 1
							$saved[ $field_group->index ][ $grouped_data['supporting_field_id'] ][] = $grouped_data['supporting_field_value'];
633
							// Reset var to the actual value
634 1
							$_new_val[ $group_index ] = $grouped_data['value'];
635 1
						}
636 1
						$new_val = $_new_val;
637 1
					} else {
638
						// Add the supporting data to the $saved array stack
639 1
						$saved[ $field_group->index ][ $new_val['supporting_field_id'] ] = $new_val['supporting_field_value'];
640
						// Reset var to the actual value
641 1
						$new_val = $new_val['value'];
642
					}
643 1
				}
644
645
				// Get old value
646 1
				$old_val = is_array( $old ) && isset( $old[ $field_group->index ][ $sub_id ] )
647 1
					? $old[ $field_group->index ][ $sub_id ]
648 1
					: false;
649
650 1
				$is_updated = ( ! empty( $new_val ) && $new_val != $old_val );
651 1
				$is_removed = ( empty( $new_val ) && ! empty( $old_val ) );
652
				// Compare values and add to `$updated` array
653 1
				if ( $is_updated || $is_removed ) {
654 1
					$this->updated[] = $base_id . '::' . $field_group->index . '::' . $sub_id;
655 1
				}
656
657
				// 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...
658 1
				$saved[ $field_group->index ][ $sub_id ] = $new_val;
659
660 1
			}
661 1
			$saved[ $field_group->index ] = array_filter( $saved[ $field_group->index ] );
662 1
		}
663 1
		$saved = array_filter( $saved );
664
665 1
		$field_group->update_data( $saved, true );
666 1
	}
667
668
	/**
669
	 * Get object id from global space if no id is provided
670
	 * @since  1.0.0
671
	 * @param  integer $object_id Object ID
672
	 * @return integer $object_id Object ID
673
	 */
674 47
	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...
675 47
		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...
676
677 47
		if ( $object_id ) {
678 18
			$this->object_id = $object_id;
679 18
			return $this->object_id;
680
		}
681
682 44
		if ( $this->object_id ) {
683 13
			return $this->object_id;
684
		}
685
686
		// Try to get our object ID from the global space
687 41
		switch ( $this->object_type() ) {
688 41
			case 'user':
689
				$object_id = isset( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : $object_id;
690
				$object_id = ! $object_id && 'user-new.php' != $pagenow && isset( $GLOBALS['user_ID'] ) ? $GLOBALS['user_ID'] : $object_id;
691
				break;
692
693 41
			case 'comment':
694
				$object_id = isset( $_REQUEST['c'] ) ? $_REQUEST['c'] : $object_id;
695
				$object_id = ! $object_id && isset( $GLOBALS['comments']->comment_ID ) ? $GLOBALS['comments']->comment_ID : $object_id;
696
				break;
697
698 41
			case 'term':
699
				$object_id = isset( $_REQUEST['tag_ID'] ) ? $_REQUEST['tag_ID'] : $object_id;
700
				break;
701
702 41
			default:
703 41
				$object_id = isset( $GLOBALS['post']->ID ) ? $GLOBALS['post']->ID : $object_id;
704 41
				$object_id = isset( $_REQUEST['post'] ) ? $_REQUEST['post'] : $object_id;
705 41
				break;
706 41
		}
707
708
		// reset to id or 0
709 41
		$this->object_id = $object_id ? $object_id : 0;
710
711 41
		return $this->object_id;
712
	}
713
714
	/**
715
	 * Sets the $object_type based on metabox settings
716
	 * @since  1.0.0
717
	 * @return string Object type
718
	 */
719 43
	public function mb_object_type() {
720
721 43
		if ( null !== $this->mb_object_type ) {
722 12
			return $this->mb_object_type;
723
		}
724
725 43
		if ( $this->is_options_page_mb() ) {
726 36
			$this->mb_object_type = 'options-page';
727 36
			return $this->mb_object_type;
728
		}
729
730 42
		if ( ! $this->prop( 'object_types' ) ) {
731 40
			$this->mb_object_type = 'post';
732 40
			return $this->mb_object_type;
733
		}
734
735 3
		$type = false;
736
		// check if 'object_types' is a string
737 3
		if ( is_string( $this->prop( 'object_types' ) ) ) {
738
			$type = $this->prop( 'object_types' );
739
		}
740
		// if it's an array of one, extract it
741 3
		elseif ( is_array( $this->prop( 'object_types' ) ) && 1 === count( $this->prop( 'object_types' ) ) ) {
742 3
			$cpts = $this->prop( 'object_types' );
743 3
			$type = is_string( end( $cpts ) )
744 3
				? end( $cpts )
745 3
				: false;
746 3
		}
747
748 3
		if ( ! $type ) {
749
			$this->mb_object_type = 'post';
750
			return $this->mb_object_type;
751
		}
752
753
		// Get our object type
754
		switch ( $type ) {
755
756 3
			case 'user':
757 3
			case 'comment':
758 3
			case 'term':
759 1
				$this->mb_object_type = $type;
760 1
				break;
761
762 2
			default:
763 2
				$this->mb_object_type = 'post';
764 2
				break;
765 2
		}
766
767 3
		return $this->mb_object_type;
768
	}
769
770
	/**
771
	 * Determines if metabox is for an options page
772
	 * @since  1.0.1
773
	 * @return boolean True/False
774
	 */
775 43
	public function is_options_page_mb() {
776 43
		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'] ) );
777
	}
778
779
	/**
780
	 * Returns the object type
781
	 * @since  1.0.0
782
	 * @return string Object type
783
	 */
784 47
	public function object_type( $object_type = '' ) {
785 47
		if ( $object_type ) {
786 17
			$this->object_type = $object_type;
787 17
			return $this->object_type;
788
		}
789
790 44
		if ( $this->object_type ) {
791 44
			return $this->object_type;
792
		}
793
794
		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...
795
796
		if ( in_array( $pagenow, array( 'user-edit.php', 'profile.php', 'user-new.php' ), true ) ) {
797
			$this->object_type = 'user';
798
799
		} elseif ( in_array( $pagenow, array( 'edit-comments.php', 'comment.php' ), true ) ) {
800
			$this->object_type = 'comment';
801
802
		} elseif ( 'edit-tags.php' == $pagenow ) {
803
			$this->object_type = 'term';
804
805
		} else {
806
			$this->object_type = 'post';
807
		}
808
809
		return $this->object_type;
810
	}
811
812
	/**
813
	 * Get metabox property and optionally set a fallback
814
	 * @since  2.0.0
815
	 * @param  string $property Metabox config property to retrieve
816
	 * @param  mixed  $fallback Fallback value to set if no value found
817
	 * @return mixed            Metabox config property value or false
818
	 */
819 43
	public function prop( $property, $fallback = null ) {
820 43
		if ( array_key_exists( $property, $this->meta_box ) ) {
821 43
			return $this->meta_box[ $property ];
822 1
		} elseif ( $fallback ) {
823 1
			return $this->meta_box[ $property ] = $fallback;
824
		}
825
	}
826
827
	/**
828
	 * Get a field object
829
	 *
830
	 * @since  2.0.3
831
	 *
832
	 * @param  string|array|CMB2_Field $field       Metabox field id or field config array or CMB2_Field object
833
	 * @param  CMB2_Field              $field_group (optional) CMB2_Field object (group parent)
834
	 *
835
	 * @return CMB2_Field|false CMB2_Field object (or false)
836
	 */
837 15
	public function get_field( $field, $field_group = null ) {
838 15
		if ( is_a( $field, 'CMB2_Field' ) ) {
839
			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...
840
		}
841
842 15
		$field_id = is_string( $field ) ? $field : $field['id'];
843
844 15
		$parent_field_id = ! empty( $field_group ) ? $field_group->id() : '';
845 15
		$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...
846
847 15
		if ( ! $ids ) {
848
			return false;
849
		}
850
851 15
		list( $field_id, $sub_field_id ) = $ids;
852
853 15
		$index = implode( '', $ids ) . ( $field_group ? $field_group->index : '' );
854 15
		if ( array_key_exists( $index, $this->fields ) ) {
855 3
			return $this->fields[ $index ];
856
		}
857
858 13
		$this->fields[ $index ] = new CMB2_Field( $this->get_field_args( $field_id, $field, $sub_field_id, $field_group ) );
859
860 13
		return $this->fields[ $index ];
861
	}
862
863
	/**
864
	 * Handles determining which type of arguments to pass to CMB2_Field
865
	 * @since  2.0.7
866
	 * @param  mixed  $field_id     Field (or group field) ID
867
	 * @param  mixed  $field_args   Array of field arguments
868
	 * @param  mixed  $sub_field_id Sub field ID (if field_group exists)
869
	 * @param  mixed  $field_group  If a sub-field, will be the parent group CMB2_Field object
870
	 * @return array                Array of CMB2_Field arguments
871
	 */
872 13
	public function get_field_args( $field_id, $field_args, $sub_field_id, $field_group ) {
873
874
		// Check if group is passed and if fields were added in the old-school fields array
875 13
		if ( $field_group && ( $sub_field_id || 0 === $sub_field_id ) ) {
876
877
			// Update the fields array w/ any modified properties inherited from the group field
878 2
			$this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ] = $field_args;
879
880
			return array(
881 2
				'field_args'  => $field_args,
882 2
				'group_field' => $field_group,
883 2
			);
884
885
		}
886
887 13
		if ( is_array( $field_args ) ) {
888 2
			$this->meta_box['fields'][ $field_id ] = array_merge( $field_args, $this->meta_box['fields'][ $field_id ] );
889 2
		}
890
891
		return array(
892 13
			'field_args'  => $this->meta_box['fields'][ $field_id ],
893 13
			'object_type' => $this->object_type(),
894 13
			'object_id'   => $this->object_id(),
895 13
		);
896
	}
897
898
	/**
899
	 * When fields are added in the old-school way, intitate them as they should be
900
	 * @since 2.1.0
901
	 * @param array $fields          Array of fields to add
902
	 * @param mixed $parent_field_id Parent field id or null
903
	 */
904 41
	protected function add_fields( $fields, $parent_field_id = null ) {
905 41
		foreach ( $fields as $field ) {
906
907 41
			$sub_fields = false;
908 41
			if ( array_key_exists( 'fields', $field ) ) {
909
				$sub_fields = $field['fields'];
910
				unset( $field['fields'] );
911
			}
912
913
			$field_id = $parent_field_id
914 41
				? $this->add_group_field( $parent_field_id, $field )
915 41
				: $this->add_field( $field );
916
917 41
			if ( $sub_fields ) {
918
				$this->add_fields( $sub_fields, $field_id );
919
			}
920 41
		}
921 41
	}
922
923
	/**
924
	 * Add a field to the metabox
925
	 * @since  2.0.0
926
	 * @param  array  $field           Metabox field config array
927
	 * @param  int    $position        (optional) Position of metabox. 1 for first, etc
928
	 * @return mixed                   Field id or false
929
	 */
930 43
	public function add_field( array $field, $position = 0 ) {
931 43
		if ( ! is_array( $field ) || ! array_key_exists( 'id', $field ) ) {
932
			return false;
933
		}
934
935 43
		if ( 'oembed' === $field['type'] ) {
936
			// Initiate oembed Ajax hooks
937 1
			cmb2_ajax();
938 1
		}
939
940 43
		$this->_add_field_to_array(
941 43
			$field,
942 43
			$this->meta_box['fields'],
943
			$position
944 43
		);
945
946 43
		return $field['id'];
947
	}
948
949
	/**
950
	 * Add a field to a group
951
	 * @since  2.0.0
952
	 * @param  string $parent_field_id The field id of the group field to add the field
953
	 * @param  array  $field           Metabox field config array
954
	 * @param  int    $position        (optional) Position of metabox. 1 for first, etc
955
	 * @return mixed                   Array of parent/field ids or false
956
	 */
957 3
	public function add_group_field( $parent_field_id, array $field, $position = 0 ) {
958 3
		if ( ! array_key_exists( $parent_field_id, $this->meta_box['fields'] ) ) {
959
			return false;
960
		}
961
962 3
		$parent_field = $this->meta_box['fields'][ $parent_field_id ];
963
964 3
		if ( 'group' !== $parent_field['type'] ) {
965
			return false;
966
		}
967
968 3
		if ( ! isset( $parent_field['fields'] ) ) {
969 3
			$this->meta_box['fields'][ $parent_field_id ]['fields'] = array();
970 3
		}
971
972 3
		$this->_add_field_to_array(
973 3
			$field,
974 3
			$this->meta_box['fields'][ $parent_field_id ]['fields'],
975
			$position
976 3
		);
977
978 3
		return array( $parent_field_id, $field['id'] );
979
	}
980
981
	/**
982
	 * Add a field array to a fields array in desired position
983
	 * @since 2.0.2
984
	 * @param array   $field    Metabox field config array
985
	 * @param array   &$fields  Array (passed by reference) to append the field (array) to
986
	 * @param integer $position Optionally specify a position in the array to be inserted
987
	 */
988 43
	protected function _add_field_to_array( $field, &$fields, $position = 0 ) {
989 43
		if ( $position ) {
990 1
			cmb2_utils()->array_insert( $fields, array( $field['id'] => $field ), $position );
991 1
		} else {
992 43
			$fields[ $field['id'] ] = $field;
993
		}
994 43
	}
995
996
	/**
997
	 * Remove a field from the metabox
998
	 * @since 2.0.0
999
	 * @param  string $field_id        The field id of the field to remove
1000
	 * @param  string $parent_field_id (optional) The field id of the group field to remove field from
1001
	 * @return bool                    True if field was removed
1002
	 */
1003 2
	public function remove_field( $field_id, $parent_field_id = '' ) {
1004 2
		$ids = $this->get_field_ids( $field_id, $parent_field_id );
1005
1006 2
		if ( ! $ids ) {
1007
			return false;
1008
		}
1009
1010 2
		list( $field_id, $sub_field_id ) = $ids;
1011
1012 2
		unset( $this->fields[ implode( '', $ids ) ] );
1013
1014 2
		if ( ! $sub_field_id ) {
1015 1
			unset( $this->meta_box['fields'][ $field_id ] );
1016 1
			return true;
1017
		}
1018
1019 1
		if ( isset( $this->fields[ $field_id ]->args['fields'][ $sub_field_id ] ) ) {
1020 1
			unset( $this->fields[ $field_id ]->args['fields'][ $sub_field_id ] );
1021 1
		}
1022 1
		if ( isset( $this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ] ) ) {
1023 1
			unset( $this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ] );
1024 1
		}
1025 1
		return true;
1026
	}
1027
1028
	/**
1029
	 * Update or add a property to a field
1030
	 * @since  2.0.0
1031
	 * @param  string $field_id        Field id
1032
	 * @param  string $property        Field property to set/update
1033
	 * @param  mixed  $value           Value to set the field property
1034
	 * @param  string $parent_field_id (optional) The field id of the group field to remove field from
1035
	 * @return mixed                   Field id. Strict compare to false, as success can return a falsey value (like 0)
1036
	 */
1037 4
	public function update_field_property( $field_id, $property, $value, $parent_field_id = '' ) {
1038 4
		$ids = $this->get_field_ids( $field_id, $parent_field_id );
1039
1040 4
		if ( ! $ids ) {
1041 2
			return false;
1042
		}
1043
1044 2
		list( $field_id, $sub_field_id ) = $ids;
1045
1046 2
		if ( ! $sub_field_id ) {
1047 2
			$this->meta_box['fields'][ $field_id ][ $property ] = $value;
1048 2
			return $field_id;
1049
		}
1050
1051
		$this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ][ $property ] = $value;
1052
		return $field_id;
1053
	}
1054
1055
	/**
1056
	 * Check if field ids match a field and return the index/field id
1057
	 * @since  2.0.2
1058
	 * @param  string  $field_id        Field id
1059
	 * @param  string  $parent_field_id (optional) Parent field id
1060
	 * @return mixed                    Array of field/parent ids, or false
1061
	 */
1062 19
	public function get_field_ids( $field_id, $parent_field_id = '' ) {
1063 19
		$sub_field_id = $parent_field_id ? $field_id : '';
1064 19
		$field_id     = $parent_field_id ? $parent_field_id : $field_id;
1065 19
		$fields       =& $this->meta_box['fields'];
1066
1067 19
		if ( ! array_key_exists( $field_id, $fields ) ) {
1068 2
			$field_id = $this->search_old_school_array( $field_id, $fields );
1069 2
		}
1070
1071 19
		if ( false === $field_id ) {
1072 2
			return false;
1073
		}
1074
1075 17
		if ( ! $sub_field_id ) {
1076 17
			return array( $field_id, $sub_field_id );
1077
		}
1078
1079 3
		if ( 'group' !== $fields[ $field_id ]['type'] ) {
1080
			return false;
1081
		}
1082
1083 3
		if ( ! array_key_exists( $sub_field_id, $fields[ $field_id ]['fields'] ) ) {
1084
			$sub_field_id = $this->search_old_school_array( $sub_field_id, $fields[ $field_id ]['fields'] );
1085
		}
1086
1087 3
		return false === $sub_field_id ? false : array( $field_id, $sub_field_id );
1088
	}
1089
1090
	/**
1091
	 * When using the old array filter, it is unlikely field array indexes will be the field id
1092
	 * @since  2.0.2
1093
	 * @param  string $field_id The field id
1094
	 * @param  array  $fields   Array of fields to search
1095
	 * @return mixed            Field index or false
1096
	 */
1097 2
	public function search_old_school_array( $field_id, $fields ) {
1098 2
		$ids = wp_list_pluck( $fields, 'id' );
1099 2
		$index = array_search( $field_id, $ids );
1100 2
		return false !== $index ? $index : false;
1101
	}
1102
1103
	/**
1104
	 * Determine whether this cmb object should show, based on the 'show_on_cb' callback.
1105
	 *
1106
	 * @since 2.0.9
1107
	 *
1108
	 * @return bool Whether this cmb should be shown.
1109
	 */
1110 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...
1111
		// Default to showing this cmb
1112
		$show = true;
1113
1114
		// Use the callback to determine showing the cmb, if it exists
1115
		if ( is_callable( $this->prop( 'show_on_cb' ) ) ) {
1116
			$show = (bool) call_user_func( $this->prop( 'show_on_cb' ), $this );
1117
		}
1118
1119
		return $show;
1120
	}
1121
1122
	/**
1123
	 * Generate a unique nonce field for each registered meta_box
1124
	 * @since  2.0.0
1125
	 * @return string unique nonce hidden input
1126
	 */
1127 1
	public function nonce_field() {
1128 1
		wp_nonce_field( $this->nonce(), $this->nonce(), false, true );
1129 1
	}
1130
1131
	/**
1132
	 * Generate a unique nonce for each registered meta_box
1133
	 * @since  2.0.0
1134
	 * @return string unique nonce string
1135
	 */
1136 1
	public function nonce() {
1137 1
		if ( $this->generated_nonce ) {
1138 1
			return $this->generated_nonce;
1139
		}
1140 1
		$this->generated_nonce = sanitize_html_class( 'nonce_' . basename( __FILE__ ) . $this->cmb_id );
1141 1
		return $this->generated_nonce;
1142
	}
1143
1144
	/**
1145
	 * Magic getter for our object.
1146
	 * @param string $field
1147
	 * @throws Exception Throws an exception if the field is invalid.
1148
	 * @return mixed
1149
	 */
1150 43
	public function __get( $field ) {
1151
		switch ( $field ) {
1152 43
			case 'cmb_id':
1153 43
			case 'meta_box':
1154 43
			case 'updated':
1155 43
				return $this->{$field};
1156 3
			case 'object_id':
1157 1
				return $this->object_id();
1158 2
			default:
1159 2
				throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field );
1160 2
		}
1161
	}
1162
1163
}
1164