Completed
Push — trunk ( df6be1...704260 )
by Justin
05:04
created

CMB2::save_group()   C

Complexity

Conditions 16
Paths 195

Size

Total Lines 70
Code Lines 39

Duplication

Lines 3
Ratio 4.29 %

Code Coverage

Tests 44
CRAP Score 16.0028

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 3
loc 70
ccs 44
cts 45
cp 0.9778
rs 5.1599
cc 16
eloc 39
nc 195
nop 1
crap 16.0028

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 CMB2 instance 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 44
	public function __construct( $meta_box, $object_id = 0 ) {
120
121 44
		if ( empty( $meta_box['id'] ) ) {
122 1
			wp_die( __( 'Metabox configuration is required to have an ID parameter', 'cmb2' ) );
123
		}
124
125 44
		$this->meta_box = wp_parse_args( $meta_box, $this->mb_defaults );
126 44
		$this->meta_box['fields'] = array();
127
128 44
		$this->object_id( $object_id );
129 44
		$this->mb_object_type();
130 44
		$this->cmb_id = $meta_box['id'];
131
132 44
		if ( ! empty( $meta_box['fields'] ) && is_array( $meta_box['fields'] ) ) {
133 41
			$this->add_fields( $meta_box['fields'] );
134 41
		}
135
136 44
		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 44
		do_action( "cmb2_init_{$this->cmb_id}", $this );
146 44
	}
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( $field_args );
276
277
		} else {
278
279 1
			$field_args['show_names'] = $this->prop( 'show_names' );
280
281
			// Render default fields
282 1
			$field = $this->get_field( $field_args )->render_field();
283
		}
284
285 1
		return $field;
286
	}
287
288
	/**
289
	 * Render a repeatable group.
290
	 * @param array $args Array of field arguments for a group field parent.
291
	 * @return CMB2_Field|null Group field object.
292
	 */
293 2
	public function render_group( $args ) {
294
295 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...
296
			return;
297
		}
298
299 2
		$field_group = $this->get_field( $args );
300
301
		// If field is requesting to be conditionally shown
302 2
		if ( ! $field_group || ! $field_group->should_show() ) {
303
			return;
304
		}
305
306 2
		$desc            = $field_group->args( 'description' );
307 2
		$label           = $field_group->args( 'name' );
308 2
		$sortable        = $field_group->options( 'sortable' ) ? ' sortable' : ' non-sortable';
309 2
		$repeat_class    = $field_group->args( 'repeatable' ) ? ' repeatable' : ' non-repeatable';
310 2
		$group_val       = (array) $field_group->value();
311 2
		$nrows           = count( $group_val );
312 2
		$remove_disabled = $nrows <= 1 ? 'disabled="disabled" ' : '';
313 2
		$field_group->index = 0;
314
315 2
		$field_group->peform_param_callback( 'before_group' );
316
317 2
		echo '<div class="cmb-row cmb-repeat-group-wrap ', $field_group->row_classes(), '"><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%;">';
318
319 2
		if ( $desc || $label ) {
320 2
			$class = $desc ? ' cmb-group-description' : '';
321 2
			echo '<div class="cmb-row', $class, '"><div class="cmb-th">';
322 2
				if ( $label ) {
323 2
					echo '<h2 class="cmb-group-name">', $label, '</h2>';
324 2
				}
325 2
				if ( $desc ) {
326 1
					echo '<p class="cmb2-metabox-description">', $desc, '</p>';
327 1
				}
328 2
			echo '</div></div>';
329 2
		}
330
331 2
		if ( ! empty( $group_val ) ) {
332
333
			foreach ( $group_val as $group_key => $field_id ) {
334
				$this->render_group_row( $field_group, $remove_disabled );
335
				$field_group->index++;
336
			}
337
		} else {
338 2
			$this->render_group_row( $field_group, $remove_disabled );
339
		}
340
341 2
		if ( $field_group->args( 'repeatable' ) ) {
342 1
			echo '<div class="cmb-row"><div class="cmb-td"><p class="cmb-add-row"><button type="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>';
343 1
		}
344
345 2
		echo '</div></div></div>';
346
347 2
		$field_group->peform_param_callback( 'after_group' );
348
349 2
		return $field_group;
350
	}
351
352
	/**
353
	 * Render a repeatable group row
354
	 * @since  1.0.2
355
	 * @param  CMB2_Field $field_group  CMB2_Field group field object
356
	 * @param  string  $remove_disabled Attribute string to disable the remove button
357
	 */
358 2
	public function render_group_row( $field_group, $remove_disabled ) {
359
360 2
		$field_group->peform_param_callback( 'before_group_row' );
361 2
		$closed_class = $field_group->options( 'closed' ) ? ' closed' : '';
362
363
		echo '
364 2
		<div class="postbox cmb-row cmb-repeatable-grouping', $closed_class, '" data-iterator="', $field_group->index, '">';
365
366 2
			if ( $field_group->args( 'repeatable' ) ) {
367 1
				echo '<button type="button" ', $remove_disabled, 'data-selector="', $field_group->id(), '_repeat" class="dashicons-before dashicons-no-alt cmb-remove-group-row"></button>';
368 1
			}
369
370
			echo '
371 2
			<div class="cmbhandle" title="' , __( 'Click to toggle', 'cmb2' ), '"><br></div>
372 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...
373
374
			<div class="inside cmb-td cmb-nested cmb-field-list">';
375
				// Loop and render repeatable group fields
376 2
				foreach ( array_values( $field_group->args( 'fields' ) ) as $field_args ) {
377 2
					if ( 'hidden' == $field_args['type'] ) {
378
379
						// Save rendering for after the metabox
380
						$this->add_hidden_field( $field_args, $field_group );
381
382
					} else {
383
384 2
						$field_args['show_names'] = $field_group->args( 'show_names' );
385 2
						$field_args['context']    = $field_group->args( 'context' );
386
387 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...
388
					}
389 2
				}
390 2
				if ( $field_group->args( 'repeatable' ) ) {
391
					echo '
392
					<div class="cmb-row cmb-remove-field-row">
393
						<div class="cmb-remove-row">
394 1
							<button type="button" ', $remove_disabled, 'data-selector="', $field_group->id(), '_repeat" class="button cmb-remove-group-row alignright">', $field_group->options( 'remove_button' ), '</button>
395
						</div>
396
					</div>
397
					';
398 1
				}
399
			echo '
400
			</div>
401
		</div>
402 2
		';
403
404 2
		$field_group->peform_param_callback( 'after_group_row' );
405 2
	}
406
407
	/**
408
	 * Add a hidden field to the list of hidden fields to be rendered later
409
	 * @since 2.0.0
410
	 * @param array  $field_args Array of field arguments to be passed to CMB2_Field
411
	 */
412
	public function add_hidden_field( $field_args, $field_group = null ) {
413
		if ( isset( $field_args['field_args'] ) ) {
414
			// For back-compatibility.
415
			$field = new CMB2_Field( $field_args );
416
		} else {
417
			$field = $this->get_new_field( $field_args, $field_group );
418
		}
419
420
		$this->hidden_fields[] = new CMB2_Types( $field );
421
422
		return $field;
423
	}
424
425
	/**
426
	 * Loop through and output hidden fields
427
	 * @since  2.0.0
428
	 */
429 1
	public function render_hidden_fields() {
430 1
		if ( ! empty( $this->hidden_fields ) ) {
431
			foreach ( $this->hidden_fields as $hidden ) {
432
				$hidden->render();
433
			}
434
		}
435 1
	}
436
437
	/**
438
	 * Returns array of sanitized field values (without saving them)
439
	 * @since  2.0.3
440
	 * @param  array  $data_to_sanitize Array of field_id => value data for sanitizing (likely $_POST data).
441
	 */
442 2
	public function get_sanitized_values( array $data_to_sanitize ) {
443 2
		$this->data_to_save = $data_to_sanitize;
444 2
		$stored_id          = $this->object_id();
445
446
		// We do this So CMB will sanitize our data for us, but not save it
447 2
		$this->object_id( '_' );
448
449
		// Ensure temp. data store is empty
450 2
		cmb2_options( 0 )->set();
451
452
		// Process/save fields
453 2
		$this->process_fields();
454
455
		// Get data from temp. data store
456 2
		$sanitized_values = cmb2_options( 0 )->get_options();
457
458
		// Empty out temp. data store again
459 2
		cmb2_options( 0 )->set();
460
461
		// Reset the object id
462 2
		$this->object_id( $stored_id );
463
464 2
		return $sanitized_values;
465
	}
466
467
	/**
468
	 * Loops through and saves field data
469
	 * @since  1.0.0
470
	 * @param  int    $object_id    Object ID
471
	 * @param  string $object_type  Type of object being saved. (e.g., post, user, or comment)
472
	 * @param  array  $data_to_save Array of key => value data for saving. Likely $_POST data.
473
	 */
474 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...
475
476
		// Fall-back to $_POST data
477 1
		$this->data_to_save = ! empty( $data_to_save ) ? $data_to_save : $_POST;
478 1
		$object_id = $this->object_id( $object_id );
479 1
		$object_type = $this->object_type( $object_type );
480
481 1
		$this->process_fields();
482
483
		// If options page, save the updated options
484 1
		if ( 'options-page' == $object_type ) {
485 1
			cmb2_options( $object_id )->set();
486 1
		}
487
488
		/**
489
		 * Fires after all fields have been saved.
490
		 *
491
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
492
		 * 	Usually `post` (this applies to all post-types).
493
		 *  	Could also be `comment`, `user` or `options-page`.
494
		 *
495
		 * @param int    $object_id   The ID of the current object
496
		 * @param array  $cmb_id      The current box ID
497
		 * @param string $updated     Array of field ids that were updated.
498
		 *                            Will only include field ids that had values change.
499
		 * @param array  $cmb         This CMB2 object
500
		 */
501 1
		do_action( "cmb2_save_{$object_type}_fields", $object_id, $this->cmb_id, $this->updated, $this );
502
503
		/**
504
		 * Fires after all fields have been saved.
505
		 *
506
		 * The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
507
		 *
508
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
509
		 * 	Usually `post` (this applies to all post-types).
510
		 *  	Could also be `comment`, `user` or `options-page`.
511
		 *
512
		 * @param int    $object_id   The ID of the current object
513
		 * @param string $updated     Array of field ids that were updated.
514
		 *                            Will only include field ids that had values change.
515
		 * @param array  $cmb         This CMB2 object
516
		 */
517 1
		do_action( "cmb2_save_{$object_type}_fields_{$this->cmb_id}", $object_id, $this->updated, $this );
518
519 1
	}
520
521
	/**
522
	 * Process and save form fields
523
	 * @since  2.0.0
524
	 */
525 3
	public function process_fields() {
526
527
		/**
528
		 * Fires before fields have been processed/saved.
529
		 *
530
		 * The dynamic portion of the hook name, $this->cmb_id, is the meta_box id.
531
		 *
532
		 * The dynamic portion of the hook name, $object_type, refers to the metabox/form's object type
533
		 * 	Usually `post` (this applies to all post-types).
534
		 *  	Could also be `comment`, `user` or `options-page`.
535
		 *
536
		 * @param array $cmb       This CMB2 object
537
		 * @param int   $object_id The ID of the current object
538
		 */
539 3
		do_action( "cmb2_{$this->object_type()}_process_fields_{$this->cmb_id}", $this, $this->object_id() );
540
541
		// Remove the show_on properties so saving works
542 3
		$this->prop( 'show_on', array() );
543
544
		// save field ids of those that are updated
545 3
		$this->updated = array();
546
547 3
		foreach ( $this->prop( 'fields' ) as $field_args ) {
548 3
			$this->process_field( $field_args );
549 3
		}
550 3
	}
551
552
	/**
553
	 * Process and save a field
554
	 * @since  2.0.0
555
	 * @param  array  $field_args Array of field arguments
556
	 */
557 3
	public function process_field( $field_args ) {
558
559 3
		switch ( $field_args['type'] ) {
560
561 3
			case 'group':
562 1
				$this->save_group( $field_args );
563 1
				break;
564
565 2
			case 'title':
566
				// Don't process title fields
567
				break;
568
569 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...
570
571 2
				$field = $this->get_new_field( $field_args );
572
573 2
				if ( $field->save_field_from_data( $this->data_to_save ) ) {
574 2
					$this->updated[] = $field->id();
575 2
				}
576
577 2
				break;
578 3
		}
579
580 3
	}
581
582
	/**
583
	 * Save a repeatable group
584
	 */
585 1
	public function save_group( $args ) {
586
587 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...
588
			return;
589
		}
590
591 1
		$field_group        = $this->get_new_field( $args );
592 1
		$base_id            = $field_group->id();
593 1
		$old                = $field_group->get_data();
594
		// Check if group field has sanitization_cb
595 1
		$group_vals         = $field_group->sanitization_cb( $this->data_to_save[ $base_id ] );
596 1
		$saved              = array();
597 1
		$field_group->index = 0;
598 1
		$field_group->data_to_save = $this->data_to_save;
599
600 1
		foreach ( array_values( $field_group->fields() ) as $field_args ) {
601
602 1
			$field  = $this->get_new_field( $field_args, $field_group );
603 1
			$sub_id = $field->id( true );
604
605 1
			foreach ( (array) $group_vals as $field_group->index => $post_vals ) {
606
607
				// Get value
608 1
				$new_val = isset( $group_vals[ $field_group->index ][ $sub_id ] )
609 1
					? $group_vals[ $field_group->index ][ $sub_id ]
610 1
					: false;
611
612
				// Sanitize
613 1
				$new_val = $field->sanitization_cb( $new_val );
614
615 1
				if ( is_array( $new_val ) && $field->args( 'has_supporting_data' ) ) {
616 1
					if ( $field->args( 'repeatable' ) ) {
617 1
						$_new_val = array();
618 1
						foreach ( $new_val as $group_index => $grouped_data ) {
619
							// Add the supporting data to the $saved array stack
620 1
							$saved[ $field_group->index ][ $grouped_data['supporting_field_id'] ][] = $grouped_data['supporting_field_value'];
621
							// Reset var to the actual value
622 1
							$_new_val[ $group_index ] = $grouped_data['value'];
623 1
						}
624 1
						$new_val = $_new_val;
625 1
					} else {
626
						// Add the supporting data to the $saved array stack
627 1
						$saved[ $field_group->index ][ $new_val['supporting_field_id'] ] = $new_val['supporting_field_value'];
628
						// Reset var to the actual value
629 1
						$new_val = $new_val['value'];
630
					}
631 1
				}
632
633
				// Get old value
634 1
				$old_val = is_array( $old ) && isset( $old[ $field_group->index ][ $sub_id ] )
635 1
					? $old[ $field_group->index ][ $sub_id ]
636 1
					: false;
637
638 1
				$is_updated = ( ! empty( $new_val ) && $new_val != $old_val );
639 1
				$is_removed = ( empty( $new_val ) && ! empty( $old_val ) );
640
				// Compare values and add to `$updated` array
641 1
				if ( $is_updated || $is_removed ) {
642 1
					$this->updated[] = $base_id . '::' . $field_group->index . '::' . $sub_id;
643 1
				}
644
645
				// 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...
646 1
				$saved[ $field_group->index ][ $sub_id ] = $new_val;
647
648 1
			}
649 1
			$saved[ $field_group->index ] = array_filter( $saved[ $field_group->index ] );
650 1
		}
651 1
		$saved = array_filter( $saved );
652
653 1
		$field_group->update_data( $saved, true );
654 1
	}
655
656
	/**
657
	 * Get object id from global space if no id is provided
658
	 * @since  1.0.0
659
	 * @param  integer $object_id Object ID
660
	 * @return integer $object_id Object ID
661
	 */
662 48
	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...
663 48
		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...
664
665 48
		if ( $object_id ) {
666 18
			$this->object_id = $object_id;
667 18
			return $this->object_id;
668
		}
669
670 45
		if ( $this->object_id ) {
671 13
			return $this->object_id;
672
		}
673
674
		// Try to get our object ID from the global space
675 42
		switch ( $this->object_type() ) {
676 42
			case 'user':
677
				$object_id = isset( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : $object_id;
678
				$object_id = ! $object_id && 'user-new.php' != $pagenow && isset( $GLOBALS['user_ID'] ) ? $GLOBALS['user_ID'] : $object_id;
679
				break;
680
681 42
			case 'comment':
682
				$object_id = isset( $_REQUEST['c'] ) ? $_REQUEST['c'] : $object_id;
683
				$object_id = ! $object_id && isset( $GLOBALS['comments']->comment_ID ) ? $GLOBALS['comments']->comment_ID : $object_id;
684
				break;
685
686 42
			case 'term':
687
				$object_id = isset( $_REQUEST['tag_ID'] ) ? $_REQUEST['tag_ID'] : $object_id;
688
				break;
689
690 42
			default:
691 42
				$object_id = isset( $GLOBALS['post']->ID ) ? $GLOBALS['post']->ID : $object_id;
692 42
				$object_id = isset( $_REQUEST['post'] ) ? $_REQUEST['post'] : $object_id;
693 42
				break;
694 42
		}
695
696
		// reset to id or 0
697 42
		$this->object_id = $object_id ? $object_id : 0;
698
699 42
		return $this->object_id;
700
	}
701
702
	/**
703
	 * Sets the $object_type based on metabox settings
704
	 * @since  1.0.0
705
	 * @return string Object type
706
	 */
707 44
	public function mb_object_type() {
708
709 44
		if ( null !== $this->mb_object_type ) {
710 12
			return $this->mb_object_type;
711
		}
712
713 44
		if ( $this->is_options_page_mb() ) {
714 36
			$this->mb_object_type = 'options-page';
715 36
			return $this->mb_object_type;
716
		}
717
718 43
		if ( ! $this->prop( 'object_types' ) ) {
719 40
			$this->mb_object_type = 'post';
720 40
			return $this->mb_object_type;
721
		}
722
723 4
		$type = false;
724
		// check if 'object_types' is a string
725 4
		if ( is_string( $this->prop( 'object_types' ) ) ) {
726
			$type = $this->prop( 'object_types' );
727
		}
728
		// if it's an array of one, extract it
729 4
		elseif ( is_array( $this->prop( 'object_types' ) ) && 1 === count( $this->prop( 'object_types' ) ) ) {
730 4
			$cpts = $this->prop( 'object_types' );
731 4
			$type = is_string( end( $cpts ) )
732 4
				? end( $cpts )
733 4
				: false;
734 4
		}
735
736 4
		if ( ! $type ) {
737
			$this->mb_object_type = 'post';
738
			return $this->mb_object_type;
739
		}
740
741
		// Get our object type
742
		switch ( $type ) {
743
744 4
			case 'user':
745 4
			case 'comment':
746 4
			case 'term':
747 1
				$this->mb_object_type = $type;
748 1
				break;
749
750 3
			default:
751 3
				$this->mb_object_type = 'post';
752 3
				break;
753 3
		}
754
755 4
		return $this->mb_object_type;
756
	}
757
758
	/**
759
	 * Determines if metabox is for an options page
760
	 * @since  1.0.1
761
	 * @return boolean True/False
762
	 */
763 44
	public function is_options_page_mb() {
764 44
		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'] ) );
765
	}
766
767
	/**
768
	 * Returns the object type
769
	 * @since  1.0.0
770
	 * @return string Object type
771
	 */
772 48
	public function object_type( $object_type = '' ) {
773 48
		if ( $object_type ) {
774 18
			$this->object_type = $object_type;
775 18
			return $this->object_type;
776
		}
777
778 45
		if ( $this->object_type ) {
779 45
			return $this->object_type;
780
		}
781
782
		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...
783
784
		if ( in_array( $pagenow, array( 'user-edit.php', 'profile.php', 'user-new.php' ), true ) ) {
785
			$this->object_type = 'user';
786
787
		} elseif ( in_array( $pagenow, array( 'edit-comments.php', 'comment.php' ), true ) ) {
788
			$this->object_type = 'comment';
789
790
		} elseif ( 'edit-tags.php' == $pagenow ) {
791
			$this->object_type = 'term';
792
793
		} else {
794
			$this->object_type = 'post';
795
		}
796
797
		return $this->object_type;
798
	}
799
800
	/**
801
	 * Get metabox property and optionally set a fallback
802
	 * @since  2.0.0
803
	 * @param  string $property Metabox config property to retrieve
804
	 * @param  mixed  $fallback Fallback value to set if no value found
805
	 * @return mixed            Metabox config property value or false
806
	 */
807 44
	public function prop( $property, $fallback = null ) {
808 44
		if ( array_key_exists( $property, $this->meta_box ) ) {
809 44
			return $this->meta_box[ $property ];
810 1
		} elseif ( $fallback ) {
811 1
			return $this->meta_box[ $property ] = $fallback;
812
		}
813
	}
814
815
	/**
816
	 * Get a field object
817
	 * @since  2.0.3
818
	 * @param  string|array|CMB2_Field $field       Metabox field id or field config array or CMB2_Field object
819
	 * @param  CMB2_Field              $field_group (optional) CMB2_Field object (group parent)
820
	 * @return CMB2_Field|false CMB2_Field object (or false)
821
	 */
822 15
	public function get_field( $field, $field_group = null ) {
823 15
		if ( is_a( $field, 'CMB2_Field' ) ) {
824
			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...
825
		}
826
827 15
		$field_id = is_string( $field ) ? $field : $field['id'];
828
829 15
		$parent_field_id = ! empty( $field_group ) ? $field_group->id() : '';
830 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...
831
832 15
		if ( ! $ids ) {
833
			return false;
834
		}
835
836 15
		list( $field_id, $sub_field_id ) = $ids;
837
838 15
		$index = implode( '', $ids ) . ( $field_group ? $field_group->index : '' );
839 15
		if ( array_key_exists( $index, $this->fields ) ) {
840 3
			return $this->fields[ $index ];
841
		}
842
843 13
		$this->fields[ $index ] = new CMB2_Field( $this->get_field_args( $field_id, $field, $sub_field_id, $field_group ) );
844
845 13
		return $this->fields[ $index ];
846
	}
847
848
	/**
849
	 * Handles determining which type of arguments to pass to CMB2_Field
850
	 * @since  2.0.7
851
	 * @param  mixed  $field_id     Field (or group field) ID
852
	 * @param  mixed  $field_args   Array of field arguments
853
	 * @param  mixed  $sub_field_id Sub field ID (if field_group exists)
854
	 * @param  mixed  $field_group  If a sub-field, will be the parent group CMB2_Field object
855
	 * @return array                Array of CMB2_Field arguments
856
	 */
857 13
	public function get_field_args( $field_id, $field_args, $sub_field_id, $field_group ) {
858
859
		// Check if group is passed and if fields were added in the old-school fields array
860 13
		if ( $field_group && ( $sub_field_id || 0 === $sub_field_id ) ) {
861
862
			// Update the fields array w/ any modified properties inherited from the group field
863 2
			$this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ] = $field_args;
864
865 2
			return $this->get_default_args( $field_args, $field_group );
866
		}
867
868 13
		if ( is_array( $field_args ) ) {
869 2
			$this->meta_box['fields'][ $field_id ] = array_merge( $field_args, $this->meta_box['fields'][ $field_id ] );
870 2
		}
871
872 13
		return $this->get_default_args( $this->meta_box['fields'][ $field_id ] );
873
	}
874
875
	/**
876
	 * Get default field arguments specific to this CMB2 object.
877
	 * @since  2.2.0
878
	 * @param  array      $field_args  Metabox field config array.
879
	 * @param  CMB2_Field $field_group (optional) CMB2_Field object (group parent)
880
	 * @return array                   Array of field arguments.
881
	 */
882 17
	protected function get_default_args( $field_args, $field_group = null ) {
883 17
		if ( $field_group ) {
884
			$args = array(
885 3
				'field_args'  => $field_args,
886 3
				'group_field' => $field_group,
887 3
			);
888 3
		} else {
889
			$args = array(
890 17
				'field_args'  => $field_args,
891 17
				'object_type' => $this->object_type(),
892 17
				'object_id'   => $this->object_id(),
893 17
				'cmb_id'      => $this->cmb_id,
894 17
			);
895
		}
896
897 17
		return $args;
898
	}
899
900
	/**
901
	 * Get a new field object specific to this CMB2 object.
902
	 * @since  2.2.0
903
	 * @param  array      $field_args  Metabox field config array.
904
	 * @param  CMB2_Field $field_group (optional) CMB2_Field object (group parent)
905
	 * @return CMB2_Field CMB2_Field object
906
	 */
907 5
	protected function get_new_field( $field_args, $field_group = null ) {
908 5
		return new CMB2_Field( $this->get_default_args( $field_args, $field_group ) );
909
	}
910
911
	/**
912
	 * When fields are added in the old-school way, intitate them as they should be
913
	 * @since 2.1.0
914
	 * @param array $fields          Array of fields to add
915
	 * @param mixed $parent_field_id Parent field id or null
916
	 */
917 41
	protected function add_fields( $fields, $parent_field_id = null ) {
918 41
		foreach ( $fields as $field ) {
919
920 41
			$sub_fields = false;
921 41
			if ( array_key_exists( 'fields', $field ) ) {
922
				$sub_fields = $field['fields'];
923
				unset( $field['fields'] );
924
			}
925
926
			$field_id = $parent_field_id
927 41
				? $this->add_group_field( $parent_field_id, $field )
928 41
				: $this->add_field( $field );
929
930 41
			if ( $sub_fields ) {
931
				$this->add_fields( $sub_fields, $field_id );
932
			}
933 41
		}
934 41
	}
935
936
	/**
937
	 * Add a field to the metabox
938
	 * @since  2.0.0
939
	 * @param  array  $field           Metabox field config array
940
	 * @param  int    $position        (optional) Position of metabox. 1 for first, etc
941
	 * @return mixed                   Field id or false
942
	 */
943 43
	public function add_field( array $field, $position = 0 ) {
944 43
		if ( ! is_array( $field ) || ! array_key_exists( 'id', $field ) ) {
945
			return false;
946
		}
947
948 43
		if ( 'oembed' === $field['type'] ) {
949
			// Initiate oembed Ajax hooks
950 1
			cmb2_ajax();
951 1
		}
952
953 43
		$this->_add_field_to_array(
954 43
			$field,
955 43
			$this->meta_box['fields'],
956
			$position
957 43
		);
958
959 43
		return $field['id'];
960
	}
961
962
	/**
963
	 * Add a field to a group
964
	 * @since  2.0.0
965
	 * @param  string $parent_field_id The field id of the group field to add the field
966
	 * @param  array  $field           Metabox field config array
967
	 * @param  int    $position        (optional) Position of metabox. 1 for first, etc
968
	 * @return mixed                   Array of parent/field ids or false
969
	 */
970 3
	public function add_group_field( $parent_field_id, array $field, $position = 0 ) {
971 3
		if ( ! array_key_exists( $parent_field_id, $this->meta_box['fields'] ) ) {
972
			return false;
973
		}
974
975 3
		$parent_field = $this->meta_box['fields'][ $parent_field_id ];
976
977 3
		if ( 'group' !== $parent_field['type'] ) {
978
			return false;
979
		}
980
981 3
		if ( ! isset( $parent_field['fields'] ) ) {
982 3
			$this->meta_box['fields'][ $parent_field_id ]['fields'] = array();
983 3
		}
984
985 3
		$this->_add_field_to_array(
986 3
			$field,
987 3
			$this->meta_box['fields'][ $parent_field_id ]['fields'],
988
			$position
989 3
		);
990
991 3
		return array( $parent_field_id, $field['id'] );
992
	}
993
994
	/**
995
	 * Add a field array to a fields array in desired position
996
	 * @since 2.0.2
997
	 * @param array   $field    Metabox field config array
998
	 * @param array   &$fields  Array (passed by reference) to append the field (array) to
999
	 * @param integer $position Optionally specify a position in the array to be inserted
1000
	 */
1001 43
	protected function _add_field_to_array( $field, &$fields, $position = 0 ) {
1002 43
		if ( $position ) {
1003 1
			cmb2_utils()->array_insert( $fields, array( $field['id'] => $field ), $position );
1004 1
		} else {
1005 43
			$fields[ $field['id'] ] = $field;
1006
		}
1007 43
	}
1008
1009
	/**
1010
	 * Remove a field from the metabox
1011
	 * @since 2.0.0
1012
	 * @param  string $field_id        The field id of the field to remove
1013
	 * @param  string $parent_field_id (optional) The field id of the group field to remove field from
1014
	 * @return bool                    True if field was removed
1015
	 */
1016 2
	public function remove_field( $field_id, $parent_field_id = '' ) {
1017 2
		$ids = $this->get_field_ids( $field_id, $parent_field_id );
1018
1019 2
		if ( ! $ids ) {
1020
			return false;
1021
		}
1022
1023 2
		list( $field_id, $sub_field_id ) = $ids;
1024
1025 2
		unset( $this->fields[ implode( '', $ids ) ] );
1026
1027 2
		if ( ! $sub_field_id ) {
1028 1
			unset( $this->meta_box['fields'][ $field_id ] );
1029 1
			return true;
1030
		}
1031
1032 1
		if ( isset( $this->fields[ $field_id ]->args['fields'][ $sub_field_id ] ) ) {
1033 1
			unset( $this->fields[ $field_id ]->args['fields'][ $sub_field_id ] );
1034 1
		}
1035 1
		if ( isset( $this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ] ) ) {
1036 1
			unset( $this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ] );
1037 1
		}
1038 1
		return true;
1039
	}
1040
1041
	/**
1042
	 * Update or add a property to a field
1043
	 * @since  2.0.0
1044
	 * @param  string $field_id        Field id
1045
	 * @param  string $property        Field property to set/update
1046
	 * @param  mixed  $value           Value to set the field property
1047
	 * @param  string $parent_field_id (optional) The field id of the group field to remove field from
1048
	 * @return mixed                   Field id. Strict compare to false, as success can return a falsey value (like 0)
1049
	 */
1050 4
	public function update_field_property( $field_id, $property, $value, $parent_field_id = '' ) {
1051 4
		$ids = $this->get_field_ids( $field_id, $parent_field_id );
1052
1053 4
		if ( ! $ids ) {
1054 2
			return false;
1055
		}
1056
1057 2
		list( $field_id, $sub_field_id ) = $ids;
1058
1059 2
		if ( ! $sub_field_id ) {
1060 2
			$this->meta_box['fields'][ $field_id ][ $property ] = $value;
1061 2
			return $field_id;
1062
		}
1063
1064
		$this->meta_box['fields'][ $field_id ]['fields'][ $sub_field_id ][ $property ] = $value;
1065
		return $field_id;
1066
	}
1067
1068
	/**
1069
	 * Check if field ids match a field and return the index/field id
1070
	 * @since  2.0.2
1071
	 * @param  string  $field_id        Field id
1072
	 * @param  string  $parent_field_id (optional) Parent field id
1073
	 * @return mixed                    Array of field/parent ids, or false
1074
	 */
1075 19
	public function get_field_ids( $field_id, $parent_field_id = '' ) {
1076 19
		$sub_field_id = $parent_field_id ? $field_id : '';
1077 19
		$field_id     = $parent_field_id ? $parent_field_id : $field_id;
1078 19
		$fields       =& $this->meta_box['fields'];
1079
1080 19
		if ( ! array_key_exists( $field_id, $fields ) ) {
1081 2
			$field_id = $this->search_old_school_array( $field_id, $fields );
1082 2
		}
1083
1084 19
		if ( false === $field_id ) {
1085 2
			return false;
1086
		}
1087
1088 17
		if ( ! $sub_field_id ) {
1089 17
			return array( $field_id, $sub_field_id );
1090
		}
1091
1092 3
		if ( 'group' !== $fields[ $field_id ]['type'] ) {
1093
			return false;
1094
		}
1095
1096 3
		if ( ! array_key_exists( $sub_field_id, $fields[ $field_id ]['fields'] ) ) {
1097
			$sub_field_id = $this->search_old_school_array( $sub_field_id, $fields[ $field_id ]['fields'] );
1098
		}
1099
1100 3
		return false === $sub_field_id ? false : array( $field_id, $sub_field_id );
1101
	}
1102
1103
	/**
1104
	 * When using the old array filter, it is unlikely field array indexes will be the field id
1105
	 * @since  2.0.2
1106
	 * @param  string $field_id The field id
1107
	 * @param  array  $fields   Array of fields to search
1108
	 * @return mixed            Field index or false
1109
	 */
1110 2
	public function search_old_school_array( $field_id, $fields ) {
1111 2
		$ids = wp_list_pluck( $fields, 'id' );
1112 2
		$index = array_search( $field_id, $ids );
1113 2
		return false !== $index ? $index : false;
1114
	}
1115
1116
	/**
1117
	 * Determine whether this cmb object should show, based on the 'show_on_cb' callback.
1118
	 *
1119
	 * @since 2.0.9
1120
	 *
1121
	 * @return bool Whether this cmb should be shown.
1122
	 */
1123 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...
1124
		// Default to showing this cmb
1125
		$show = true;
1126
1127
		// Use the callback to determine showing the cmb, if it exists
1128
		if ( is_callable( $this->prop( 'show_on_cb' ) ) ) {
1129
			$show = (bool) call_user_func( $this->prop( 'show_on_cb' ), $this );
1130
		}
1131
1132
		return $show;
1133
	}
1134
1135
	/**
1136
	 * Generate a unique nonce field for each registered meta_box
1137
	 * @since  2.0.0
1138
	 * @return string unique nonce hidden input
1139
	 */
1140 1
	public function nonce_field() {
1141 1
		wp_nonce_field( $this->nonce(), $this->nonce(), false, true );
1142 1
	}
1143
1144
	/**
1145
	 * Generate a unique nonce for each registered meta_box
1146
	 * @since  2.0.0
1147
	 * @return string unique nonce string
1148
	 */
1149 1
	public function nonce() {
1150 1
		if ( $this->generated_nonce ) {
1151 1
			return $this->generated_nonce;
1152
		}
1153 1
		$this->generated_nonce = sanitize_html_class( 'nonce_' . basename( __FILE__ ) . $this->cmb_id );
1154 1
		return $this->generated_nonce;
1155
	}
1156
1157
	/**
1158
	 * Magic getter for our object.
1159
	 * @param string $field
1160
	 * @throws Exception Throws an exception if the field is invalid.
1161
	 * @return mixed
1162
	 */
1163 44
	public function __get( $field ) {
1164
		switch ( $field ) {
1165 44
			case 'cmb_id':
1166 44
			case 'meta_box':
1167 44
			case 'updated':
1168 44
				return $this->{$field};
1169 3
			case 'object_id':
1170 1
				return $this->object_id();
1171 2
			default:
1172 2
				throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field );
1173 2
		}
1174
	}
1175
1176
}
1177