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

CMB2_hookup::post_hooks()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 3
eloc 9
c 1
b 0
f 1
nc 3
nop 0
dl 0
loc 13
ccs 0
cts 11
cp 0
crap 12
rs 9.4285
1
<?php
2
/**
3
 * Handles hooking CMB2 forms/metaboxes into the post/attachement/user screens
4
 * and handles hooking in and saving those fields.
5
 *
6
 * @since  2.0.0
7
 *
8
 * @category  WordPress_Plugin
9
 * @package   CMB2
10
 * @author    WebDevStudios
11
 * @license   GPL-2.0+
12
 * @link      http://webdevstudios.com
13
 */
14
class CMB2_hookup extends CMB2_Hookup_Base {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

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

namespace YourVendor;

class YourClass { }

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

Loading history...
15
16
	/**
17
	 * Only allow JS registration once
18
	 * @var   bool
19
	 * @since 2.0.7
20
	 */
21
	protected static $js_registration_done = false;
22
23
	/**
24
	 * Only allow CSS registration once
25
	 * @var   bool
26
	 * @since 2.0.7
27
	 */
28
	protected static $css_registration_done = false;
29
30
	/**
31
	 * CMB taxonomies array for term meta
32
	 * @var   array
33
	 * @since 2.2.0
34
	 */
35
	protected $taxonomies = array();
36
37
	/**
38
	 * Custom field columns.
39
	 * @var   array
40
	 * @since 2.2.2
41
	 */
42
	protected $columns = array();
43
44
	/**
45
	 * Constructor
46
	 * @since 2.0.0
47
	 * @param CMB2 $cmb The CMB2 object to hookup
48
	 */
49
	public function __construct( CMB2 $cmb ) {
50
		$this->cmb = $cmb;
51
		$this->object_type = $this->cmb->mb_object_type();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->cmb->mb_object_type() can also be of type false. However, the property $object_type is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
52
53
		$this->universal_hooks();
54
55
		if ( is_admin() ) {
56
57
			switch ( $this->object_type ) {
58
				case 'post':
59
					return $this->post_hooks();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
60
				case 'comment':
61
					return $this->comment_hooks();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
62
				case 'user':
63
					return $this->user_hooks();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
64
				case 'term':
65
					return $this->term_hooks();
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
66
			}
67
68
		}
69
	}
70
71
	public function universal_hooks() {
72
		foreach ( get_class_methods( 'CMB2_Show_Filters' ) as $filter ) {
73
			add_filter( 'cmb2_show_on', array( 'CMB2_Show_Filters', $filter ), 10, 3 );
74
		}
75
76
		if ( is_admin() ) {
77
			// register our scripts and styles for cmb
78
			$this->once( 'admin_enqueue_scripts', array( __CLASS__, 'register_scripts' ), 8 );
79
			$this->once( 'admin_enqueue_scripts', array( $this, 'do_scripts' ) );
80
81
			$this->maybe_enqueue_column_display_styles();
82
		}
83
	}
84
85
	public function post_hooks() {
86
		add_action( 'add_meta_boxes', array( $this, 'add_metaboxes' ) );
87
		add_action( 'add_attachment', array( $this, 'save_post' ) );
88
		add_action( 'edit_attachment', array( $this, 'save_post' ) );
89
		add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
90
91
		if ( $this->cmb->has_columns ) {
92
			foreach ( $this->cmb->prop( 'object_types' ) as $post_type ) {
93
				add_filter( "manage_{$post_type}_posts_columns", array( $this, 'register_column_headers' ) );
94
				add_action( "manage_{$post_type}_posts_custom_column", array( $this, 'column_display' ), 10, 2 );
95
			}
96
		}
97
	}
98
99
	public function comment_hooks() {
100
		add_action( 'add_meta_boxes_comment', array( $this, 'add_metaboxes' ) );
101
		add_action( 'edit_comment', array( $this, 'save_comment' ) );
102
103
		if ( $this->cmb->has_columns ) {
104
			add_filter( 'manage_edit-comments_columns', array( $this, 'register_column_headers' ) );
105
			add_action( 'manage_comments_custom_column', array( $this, 'column_display'  ), 10, 3 );
106
		}
107
	}
108
109
	public function user_hooks() {
110 1
		$priority = $this->get_priority();
111 1
112 1
		add_action( 'show_user_profile', array( $this, 'user_metabox' ), $priority );
113
		add_action( 'edit_user_profile', array( $this, 'user_metabox' ), $priority );
114
		add_action( 'user_new_form', array( $this, 'user_new_metabox' ), $priority );
115
116 1
		add_action( 'personal_options_update', array( $this, 'save_user' ) );
117
		add_action( 'edit_user_profile_update', array( $this, 'save_user' ) );
118 1
		add_action( 'user_register', array( $this, 'save_user' ) );
119
120 1 View Code Duplication
		if ( $this->cmb->has_columns ) {
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...
121 1
			add_filter( 'manage_users_columns', array( $this, 'register_column_headers' ) );
122 1
			add_filter( 'manage_users_custom_column', array( $this, 'return_column_display'  ), 10, 3 );
123 1
		}
124 1
	}
125 1
126 1
	public function term_hooks() {
127 1
		if ( ! function_exists( 'get_term_meta' ) ) {
128 1
			wp_die( esc_html__( 'Term Metadata is a WordPress 4.4+ feature. Please upgrade your WordPress install.', 'cmb2' ) );
129
		}
130 1
131
		if ( ! $this->cmb->prop( 'taxonomies' ) ) {
132
			wp_die( esc_html__( 'Term metaboxes configuration requires a "taxonomies" parameter.', 'cmb2' ) );
133 1
		}
134
135 1
		$this->taxonomies = (array) $this->cmb->prop( 'taxonomies' );
136
		$show_on_term_add = $this->cmb->prop( 'new_term_section' );
137 1
		$priority         = $this->get_priority( 8 );
138
139 1
		foreach ( $this->taxonomies as $taxonomy ) {
140 1
			// Display our form data
141 1
			add_action( "{$taxonomy}_edit_form", array( $this, 'term_metabox' ), $priority, 2 );
142 1
143 1
			$show_on_add = is_array( $show_on_term_add )
144 1
				? in_array( $taxonomy, $show_on_term_add )
145
				: (bool) $show_on_term_add;
146 1
147
			$show_on_add = apply_filters( "cmb2_show_on_term_add_form_{$this->cmb->cmb_id}", $show_on_add, $this->cmb );
148 1
149 1
			// Display form in add-new section (unless specified not to)
150 1
			if ( $show_on_add ) {
151 1
				add_action( "{$taxonomy}_add_form_fields", array( $this, 'term_metabox' ), $priority, 2 );
152 1
			}
153 1
154 1 View Code Duplication
			if ( $this->cmb->has_columns ) {
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...
155 1
				add_filter( "manage_edit-{$taxonomy}_columns", array( $this, 'register_column_headers' ) );
156 1
				add_filter( "manage_{$taxonomy}_custom_column", array( $this, 'return_column_display'  ), 10, 3 );
157 1
			}
158 1
		}
159 1
160 1
		add_action( 'created_term', array( $this, 'save_term' ), 10, 3 );
161 1
		add_action( 'edited_terms', array( $this, 'save_term' ), 10, 2 );
162
		add_action( 'delete_term', array( $this, 'delete_term' ), 10, 3 );
163 1
164 1
	}
165 1
166 1
	/**
167
	 * Registers styles for CMB2
168 1
	 * @since 2.0.7
169 1
	 */
170
	protected static function register_styles() {
171 1
		if ( self::$css_registration_done ) {
172 1
			return;
173 1
		}
174 1
175 1
		// Only use minified files if SCRIPT_DEBUG is off
176 1
		$min   = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
177 1
		$front = is_admin() ? '' : '-front';
178 1
		$rtl   = is_rtl() ? '-rtl' : '';
179
180 1
		// Filter required styles and register stylesheet
181
		$dependencies = apply_filters( 'cmb2_style_dependencies', array() );
182 1
		wp_register_style( 'cmb2-styles', CMB2_Utils::url( "css/cmb2{$front}{$rtl}{$min}.css" ), $dependencies );
183 1
		wp_register_style( 'cmb2-display-styles', CMB2_Utils::url( "css/cmb2-display{$rtl}{$min}.css" ), $dependencies );
184
185
		self::$css_registration_done = true;
186
	}
187
188
	/**
189
	 * Registers scripts for CMB2
190
	 * @since  2.0.7
191
	 */
192
	protected static function register_js() {
193
		if ( self::$js_registration_done ) {
194
			return;
195
		}
196
197
		$hook = is_admin() ? 'admin_footer' : 'wp_footer';
198
		add_action( $hook, array( 'CMB2_JS', 'enqueue' ), 8 );
199
200
		self::$js_registration_done = true;
201
	}
202
203
	/**
204
	 * Registers scripts and styles for CMB2
205
	 * @since  1.0.0
206
	 */
207
	public static function register_scripts() {
208
		self::register_styles();
209
		self::register_js();
210
	}
211
212
	/**
213
	 * Enqueues scripts and styles for CMB2 in admin_head.
214
	 * @since  1.0.0
215
	 */
216
	public function do_scripts( $hook ) {
217
		$hooks = array(
218
			'post.php',
219
			'post-new.php',
220
			'page-new.php',
221
			'page.php',
222
			'comment.php',
223
			'edit-tags.php',
224
			'term.php',
225
			'user-new.php',
226
			'profile.php',
227
			'user-edit.php',
228
		);
229
		// only pre-enqueue our scripts/styles on the proper pages
230
		// show_form_for_type will have us covered if we miss something here.
231
		if ( in_array( $hook, $hooks, true ) ) {
232
			if ( $this->cmb->prop( 'cmb_styles' ) ) {
233
				self::enqueue_cmb_css();
234
			}
235
			if ( $this->cmb->prop( 'enqueue_js' ) ) {
236
				self::enqueue_cmb_js();
237
			}
238
		}
239
	}
240
241
	/**
242
	 * Register the CMB2 field column headers.
243
	 * @since 2.2.2
244
	 */
245
	public function register_column_headers( $columns ) {
246
		$fields = $this->cmb->prop( 'fields' );
247
248
		foreach ( $fields as $key => $field ) {
249
			if ( ! isset( $field['column'] ) ) {
250
				continue;
251
			}
252
253
			$column = $field['column'];
254
255
			if ( false === $column['position'] ) {
256
257
				$columns[ $field['id'] ] = $column['name'];
258
259
			} else {
260
261
				$before = array_slice( $columns, 0, absint( $column['position'] ) );
262
				$before[ $field['id'] ] = $column['name'];
263
				$columns = $before + $columns;
264
			}
265
266
			$column['field'] = $field;
267
			$this->columns[ $field['id'] ] = $column;
268
		}
269
270
		return $columns;
271
	}
272
273
	/**
274
	 * The CMB2 field column display output.
275
	 * @since 2.2.2
276
	 */
277
	public function column_display( $column_name, $object_id ) {
278
		if ( isset( $this->columns[ $column_name ] ) ) {
279
 			$field = new CMB2_Field( array(
280
				'field_args'  => $this->columns[ $column_name ]['field'],
281
				'object_type' => $this->object_type,
282
				'object_id'   => $this->cmb->object_id( $object_id ),
283
				'cmb_id'      => $this->cmb->cmb_id,
284
			) );
285
286
			$this->cmb->get_field( $field )->render_column();
287
		}
288
	}
289
290
	/**
291
	 * Returns the column display.
292
	 * @since 2.2.2
293
	 */
294
	public function return_column_display( $empty, $custom_column, $object_id ) {
295
		ob_start();
296
		$this->column_display( $custom_column, $object_id );
297
		$column = ob_get_clean();
298
299
		return $column ? $column : $empty;
300
	}
301
302
	/**
303
	 * Add metaboxes (to 'post' or 'comment' object types)
304
	 * @since 1.0.0
305
	 */
306
	public function add_metaboxes() {
307
308
		if ( ! $this->show_on() ) {
309
			return;
310
		}
311
312
		/**
313
		 * To keep from registering an actual post-screen metabox,
314
		 * omit the 'title' attribute from the metabox registration array.
315
		 *
316
		 * (WordPress will not display metaboxes without titles anyway)
317
		 *
318
		 * This is a good solution if you want to output your metaboxes
319
		 * Somewhere else in the post-screen
320
		 */
321
		if ( ! $this->cmb->prop( 'title' ) ) {
322
			return;
323
		}
324
325
		foreach ( $this->cmb->prop( 'object_types' ) as $post_type ) {
326
			if ( $this->cmb->prop( 'closed' ) ) {
327
				add_filter( "postbox_classes_{$post_type}_{$this->cmb->cmb_id}", array( $this, 'close_metabox_class' ) );
328
			}
329
330
			if ( count( $this->cmb->tax_metaboxes_to_remove ) ) {
331
				$this->remove_default_tax_metaboxes( $post_type );
332
			}
333
334
			add_meta_box( $this->cmb->cmb_id, $this->cmb->prop( 'title' ), array( $this, 'metabox_callback' ), $post_type, $this->cmb->prop( 'context' ), $this->cmb->prop( 'priority' ) );
335
		}
336
	}
337
338
	/**
339
	 * Remove the specified default taxonomy metaboxes for a post-type.
340
	 * @since 2.2.3
341
	 * @param string $post_type Post type to remove the metabox for.
342
	 */
343
	protected function remove_default_tax_metaboxes( $post_type ) {
344
		foreach ( $this->cmb->tax_metaboxes_to_remove as $taxonomy ) {
345
			if ( ! taxonomy_exists( $taxonomy ) ) {
346
				continue;
347
			}
348
349
			$mb_id = is_taxonomy_hierarchical( $taxonomy ) ? "{$taxonomy}div" : "tagsdiv-{$taxonomy}";
350 1
			remove_meta_box( $mb_id, $post_type, 'side' );
351 1
		}
352
	}
353 1
354
	/**
355
	 * Add 'closed' class to metabox
356
	 * @since  2.0.0
357
	 * @param  array  $classes Array of classes
358
	 * @return array           Modified array of classes
359
	 */
360 1
	public function close_metabox_class( $classes ) {
361 1
		$classes[] = 'closed';
362 1
		return $classes;
363 1
	}
364
365
	/**
366
	 * Display metaboxes for a post or comment object
367 1
	 * @since  1.0.0
368
	 */
369
	public function metabox_callback() {
370
		$object_id = 'comment' == $this->object_type ? get_comment_ID() : get_the_ID();
371
		$this->cmb->show_form( $object_id, $this->object_type );
372
	}
373
374
	/**
375
	 * Display metaboxes for new user page
376
	 * @since  1.0.0
377
	 */
378
	public function user_new_metabox( $section ) {
0 ignored issues
show
Coding Style introduced by
user_new_metabox 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...
379
		if ( $section == $this->cmb->prop( 'new_user_section' ) ) {
380
			$object_id = $this->cmb->object_id();
381
			$this->cmb->object_id( isset( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : $object_id );
382
			$this->user_metabox();
383
		}
384
	}
385
386
	/**
387
	 * Display metaboxes for a user object
388
	 * @since  1.0.0
389
	 */
390
	public function user_metabox() {
391
		$this->show_form_for_type( 'user' );
392
	}
393
394
	/**
395
	 * Display metaboxes for a taxonomy term object
396
	 * @since  2.2.0
397
	 */
398
	public function term_metabox() {
399
		$this->show_form_for_type( 'term' );
400
	}
401
402
	/**
403
	 * Display metaboxes for an object type
404
	 * @since  2.2.0
405
	 * @param  string $type Object type
406
	 * @return void
407
	 */
408
	public function show_form_for_type( $type ) {
409
		if ( $type != $this->cmb->mb_object_type() ) {
410
			return;
411
		}
412
413
		if ( ! $this->show_on() ) {
414
			return;
415
		}
416
417
		if ( $this->cmb->prop( 'cmb_styles' ) ) {
418
			self::enqueue_cmb_css();
419
		}
420
		if ( $this->cmb->prop( 'enqueue_js' ) ) {
421
			self::enqueue_cmb_js();
422
		}
423
424
		$this->cmb->show_form( 0, $type );
425
	}
426
427
	/**
428
	 * Determines if metabox should be shown in current context
429
	 * @since  2.0.0
430
	 * @return bool Whether metabox should be added/shown
431
	 */
432
	public function show_on() {
433
		// If metabox is requesting to be conditionally shown
434
		$show = $this->cmb->should_show();
435
436
		/**
437
		 * Filter to determine if metabox should show. Default is true
438
		 *
439
		 * @param array  $show          Default is true, show the metabox
440
		 * @param mixed  $meta_box_args Array of the metabox arguments
441
		 * @param mixed  $cmb           The CMB2 instance
442
		 */
443
		$show = (bool) apply_filters( 'cmb2_show_on', $show, $this->cmb->meta_box, $this->cmb );
444
445
		return $show;
446
	}
447
448
	/**
449
	 * Get the CMB priority property set to numeric hook priority.
450
	 * @since  2.2.0
451
	 * @param  integer $default Default display hook priority.
452
	 * @return integer          Hook priority.
453
	 */
454
	public function get_priority( $default = 10 ) {
455
		$priority = $this->cmb->prop( 'priority' );
456
457
		if ( ! is_numeric( $priority ) ) {
458
			switch ( $priority ) {
459
460
				case 'high':
461
					$priority = 5;
462
					break;
463
464
				case 'low':
465
					$priority = 20;
466
					break;
467
468
				default:
469
					$priority = $default;
470
					break;
471
			}
472
		}
473
474
		return $priority;
475
	}
476
477
	/**
478
	 * Save data from post metabox
479
	 * @since  1.0.0
480
	 * @param  int    $post_id Post ID
481
	 * @param  mixed  $post    Post object
482
	 * @return null
483
	 */
484
	public function save_post( $post_id, $post = false ) {
0 ignored issues
show
Coding Style introduced by
save_post 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...
485
486
		$post_type = $post ? $post->post_type : get_post_type( $post_id );
487
488
		$do_not_pass_go = (
489
			! $this->can_save( $post_type )
490
			// check user editing permissions
491
			|| ( 'page' == $post_type && ! current_user_can( 'edit_page', $post_id ) )
492
			|| ! current_user_can( 'edit_post', $post_id )
493
		);
494
495
		if ( $do_not_pass_go ) {
496
			// do not collect $200
497
			return;
498
		}
499
500
		// take a trip to reading railroad – if you pass go collect $200
501
		$this->cmb->save_fields( $post_id, 'post', $_POST );
502
	}
503
504
	/**
505
	 * Save data from comment metabox
506
	 * @since  2.0.9
507
	 * @param  int    $comment_id Comment ID
508
	 * @return null
509
	 */
510
	public function save_comment( $comment_id ) {
0 ignored issues
show
Coding Style introduced by
save_comment 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...
511
512
		$can_edit = current_user_can( 'moderate_comments', $comment_id );
513
514
		if ( $this->can_save( get_comment_type( $comment_id ) ) && $can_edit ) {
515
			$this->cmb->save_fields( $comment_id, 'comment', $_POST );
516
		}
517
	}
518
519
	/**
520
	 * Save data from user fields
521
	 * @since  1.0.x
522
	 * @param  int   $user_id  User ID
523
	 * @return null
524
	 */
525
	public function save_user( $user_id ) {
0 ignored issues
show
Coding Style introduced by
save_user 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...
526
		// check permissions
527
		if ( $this->can_save( 'user' ) ) {
528
			$this->cmb->save_fields( $user_id, 'user', $_POST );
529
		}
530
	}
531
532
	/**
533
	 * Save data from term fields
534
	 * @since  2.2.0
535
	 * @param  int    $term_id  Term ID
536
	 * @param  int    $tt_id    Term Taxonomy ID
537
	 * @param  string $taxonomy Taxonomy
538
	 * @return null
539
	 */
540
	public function save_term( $term_id, $tt_id, $taxonomy = '' ) {
0 ignored issues
show
Coding Style introduced by
save_term 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...
541
		$taxonomy = $taxonomy ? $taxonomy : $tt_id;
542
543
		// check permissions
544
		if ( $this->taxonomy_can_save( $taxonomy ) && $this->can_save( 'term' ) ) {
545
			$this->cmb->save_fields( $term_id, 'term', $_POST );
546
		}
547
	}
548
549
	/**
550
	 * Delete term meta when a term is deleted.
551
	 * @since  2.2.0
552
	 * @param  int    $term_id  Term ID
553
	 * @param  int    $tt_id    Term Taxonomy ID
554
	 * @param  string $taxonomy Taxonomy
555
	 * @return null
556
	 */
557
	public function delete_term( $term_id, $tt_id, $taxonomy = '' ) {
558
		if ( $this->taxonomy_can_save( $taxonomy ) ) {
559
560
			foreach ( $this->cmb->prop( 'fields' ) as $field ) {
561
				$data_to_delete[ $field['id'] ] = '';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data_to_delete was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data_to_delete = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
562
			}
563
564
			$this->cmb->save_fields( $term_id, 'term', $data_to_delete );
0 ignored issues
show
Bug introduced by
The variable $data_to_delete does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
565
		}
566
	}
567
568
	/**
569
	 * Determines if the current object is able to be saved
570
	 * @since  2.0.9
571
	 * @param  string  $type Current post_type or comment_type
572
	 * @return bool          Whether object can be saved
573
	 */
574
	public function can_save( $type = '' ) {
0 ignored issues
show
Coding Style introduced by
can_save 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...
575
		return apply_filters( 'cmb2_can_save', (
576
			$this->cmb->prop( 'save_fields' )
577
			// check nonce
578
			&& isset( $_POST[ $this->cmb->nonce() ] )
579
			&& wp_verify_nonce( $_POST[ $this->cmb->nonce() ], $this->cmb->nonce() )
580
			// check if autosave
581
			&& ! ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
582
			// get the metabox types & compare it to this type
583
			&& ( $type && in_array( $type, $this->cmb->prop( 'object_types' ) ) )
584
			// Don't do updates during a switch-to-blog instance.
585
			&& ! ( is_multisite() && ms_is_switched() )
586
		) );
587
	}
588
589
	/**
590
	 * Determine if taxonomy of term being modified is cmb2-editable.
591
	 * @since  2.2.0
592
	 * @param  string $taxonomy Taxonomy of term being modified.
593
	 * @return bool             Whether taxonomy is editable.
594
	 */
595
	public function taxonomy_can_save( $taxonomy ) {
596
		if ( empty( $this->taxonomies ) || ! in_array( $taxonomy, $this->taxonomies ) ) {
597
			return false;
598
		}
599
600
		$taxonomy_object = get_taxonomy( $taxonomy );
601
		// Can the user edit this term?
602
		if ( ! isset( $taxonomy_object->cap ) || ! current_user_can( $taxonomy_object->cap->edit_terms ) ) {
603
			return false;
604
		}
605
606
		return true;
607
	}
608
609
	/**
610
	 * Enqueues the 'cmb2-display-styles' if the conditions match (has columns, on the right page, etc).
611
	 * @since  2.2.2.1
612
	 */
613
	protected function maybe_enqueue_column_display_styles() {
614
		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...
615
		if (
616
			$pagenow
617
			&& $this->cmb->has_columns
618
			&& $this->cmb->prop( 'cmb_styles' )
619
			&& in_array( $pagenow, array( 'edit.php', 'users.php', 'edit-comments.php', 'edit-tags.php' ), 1 )
620
			) {
621
			self::enqueue_cmb_css( 'cmb2-display-styles' );
622
		}
623
	}
624
625
	/**
626
	 * Includes CMB2 styles
627
	 * @since  2.0.0
628
	 */
629
	public static function enqueue_cmb_css( $handle = 'cmb2-styles' ) {
630
		if ( ! apply_filters( 'cmb2_enqueue_css', true ) ) {
631
			return false;
632
		}
633
634
		self::register_styles();
635
636
		/*
637
		 * White list the options as this method can be used as a hook callback
638
		 * and have a different argument passed.
639
		 */
640
		return wp_enqueue_style( 'cmb2-display-styles' === $handle ? $handle : 'cmb2-styles' );
641
	}
642
643
	/**
644
	 * Includes CMB2 JS
645
	 * @since  2.0.0
646
	 */
647
	public static function enqueue_cmb_js() {
648
		if ( ! apply_filters( 'cmb2_enqueue_js', true ) ) {
649
			return false;
650
		}
651
652
		self::register_js();
653
		return true;
654
	}
655
656
}
657