Completed
Push — trunk ( f02feb...6ee722 )
by Justin
07:11
created

CMB2_hookup::user_hooks()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 11

Duplication

Lines 4
Ratio 25 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 3
Bugs 1 Features 3
Metric Value
cc 2
eloc 11
c 3
b 1
f 3
nc 2
nop 0
dl 4
loc 16
ccs 0
cts 13
cp 0
crap 6
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 ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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 ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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
		$priority = $this->get_priority();
111
112
		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
		add_action( 'personal_options_update', array( $this, 'save_user' ) );
117
		add_action( 'edit_user_profile_update', array( $this, 'save_user' ) );
118
		add_action( 'user_register', array( $this, 'save_user' ) );
119
120 View Code Duplication
		if ( $this->cmb->has_columns ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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
			add_filter( 'manage_users_columns', array( $this, 'register_column_headers' ) );
122
			add_filter( 'manage_users_custom_column', array( $this, 'return_column_display'  ), 10, 3 );
123
		}
124
	}
125
126
	public function term_hooks() {
127
		if ( ! function_exists( 'get_term_meta' ) ) {
128
			wp_die( __( 'Term Metadata is a WordPress > 4.4 feature. Please upgrade your WordPress install.', 'cmb2' ) );
129
		}
130
131
		if ( ! $this->cmb->prop( 'taxonomies' ) ) {
132
			wp_die( __( 'Term metaboxes configuration requires a \'taxonomies\' parameter', 'cmb2' ) );
133
		}
134
135
		$this->taxonomies = (array) $this->cmb->prop( 'taxonomies' );
136
		$show_on_term_add = $this->cmb->prop( 'new_term_section' );
137
		$priority         = $this->get_priority( 8 );
138
139
		foreach ( $this->taxonomies as $taxonomy ) {
140
			// Display our form data
141
			add_action( "{$taxonomy}_edit_form", array( $this, 'term_metabox' ), $priority, 2 );
142
143
			$show_on_add = is_array( $show_on_term_add )
144
				? in_array( $taxonomy, $show_on_term_add )
145
				: (bool) $show_on_term_add;
146
147
			$show_on_add = apply_filters( "cmb2_show_on_term_add_form_{$this->cmb->cmb_id}", $show_on_add, $this->cmb );
148
149
			// Display form in add-new section (unless specified not to)
150
			if ( $show_on_add ) {
151
				add_action( "{$taxonomy}_add_form_fields", array( $this, 'term_metabox' ), $priority, 2 );
152
			}
153
154 View Code Duplication
			if ( $this->cmb->has_columns ) {
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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
				add_filter( "manage_edit-{$taxonomy}_columns", array( $this, 'register_column_headers' ) );
156
				add_filter( "manage_{$taxonomy}_custom_column", array( $this, 'return_column_display'  ), 10, 3 );
157
			}
158
		}
159
160
		add_action( 'created_term', array( $this, 'save_term' ), 10, 3 );
161
		add_action( 'edited_terms', array( $this, 'save_term' ), 10, 2 );
162
		add_action( 'delete_term', array( $this, 'delete_term' ), 10, 3 );
163
164
	}
165
166
	/**
167
	 * Registers styles for CMB2
168
	 * @since 2.0.7
169
	 */
170 1
	protected static function register_styles() {
171 1
		if ( self::$css_registration_done ) {
172
			return;
173
		}
174
175
		// 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
		// Filter required styles and register stylesheet
181 1
		$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 1
		self::$css_registration_done = true;
186 1
	}
187
188
	/**
189
	 * Registers scripts for CMB2
190
	 * @since  2.0.7
191
	 */
192 1
	protected static function register_js() {
193 1
		if ( self::$js_registration_done ) {
194
			return;
195
		}
196
197 1
		$hook = is_admin() ? 'admin_footer' : 'wp_footer';
198 1
		add_action( $hook, array( 'CMB2_JS', 'enqueue' ), 8 );
199
200 1
		self::$js_registration_done = true;
201 1
	}
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
			foreach ( $this->cmb->tax_metaboxes_to_remove as $taxonomy ) {
0 ignored issues
show
Documentation introduced by
The property $tax_metaboxes_to_remove is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The expression $this->cmb->tax_metaboxes_to_remove of type boolean is not traversable.
Loading history...
331
				if ( ! taxonomy_exists( $taxonomy ) ) {
332
					continue;
333
				}
334
335
				$mb_id = is_taxonomy_hierarchical( $taxonomy ) ? "{$taxonomy}div" : "tagsdiv-{$taxonomy}";
336
				remove_meta_box( $mb_id, $post_type, 'side' );
337
			}
338
339
			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' ) );
340
		}
341
	}
342
343
	/**
344
	 * Add 'closed' class to metabox
345
	 * @since  2.0.0
346
	 * @param  array  $classes Array of classes
347
	 * @return array           Modified array of classes
348
	 */
349
	public function close_metabox_class( $classes ) {
350
		$classes[] = 'closed';
351
		return $classes;
352
	}
353
354
	/**
355
	 * Display metaboxes for a post or comment object
356
	 * @since  1.0.0
357
	 */
358
	public function metabox_callback() {
359
		$object_id = 'comment' == $this->object_type ? get_comment_ID() : get_the_ID();
360
		$this->cmb->show_form( $object_id, $this->object_type );
361
	}
362
363
	/**
364
	 * Display metaboxes for new user page
365
	 * @since  1.0.0
366
	 */
367
	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...
368
		if ( $section == $this->cmb->prop( 'new_user_section' ) ) {
369
			$object_id = $this->cmb->object_id();
370
			$this->cmb->object_id( isset( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : $object_id );
371
			$this->user_metabox();
372
		}
373
	}
374
375
	/**
376
	 * Display metaboxes for a user object
377
	 * @since  1.0.0
378
	 */
379
	public function user_metabox() {
380
		$this->show_form_for_type( 'user' );
381
	}
382
383
	/**
384
	 * Display metaboxes for a taxonomy term object
385
	 * @since  2.2.0
386
	 */
387
	public function term_metabox() {
388
		$this->show_form_for_type( 'term' );
389
	}
390
391
	/**
392
	 * Display metaboxes for an object type
393
	 * @since  2.2.0
394
	 * @param  string $type Object type
395
	 * @return void
396
	 */
397
	public function show_form_for_type( $type ) {
398
		if ( $type != $this->cmb->mb_object_type() ) {
399
			return;
400
		}
401
402
		if ( ! $this->show_on() ) {
403
			return;
404
		}
405
406
		if ( $this->cmb->prop( 'cmb_styles' ) ) {
407
			self::enqueue_cmb_css();
408
		}
409
		if ( $this->cmb->prop( 'enqueue_js' ) ) {
410
			self::enqueue_cmb_js();
411
		}
412
413
		$this->cmb->show_form( 0, $type );
414
	}
415
416
	/**
417
	 * Determines if metabox should be shown in current context
418
	 * @since  2.0.0
419
	 * @return bool Whether metabox should be added/shown
420
	 */
421
	public function show_on() {
422
		// If metabox is requesting to be conditionally shown
423
		$show = $this->cmb->should_show();
424
425
		/**
426
		 * Filter to determine if metabox should show. Default is true
427
		 *
428
		 * @param array  $show          Default is true, show the metabox
429
		 * @param mixed  $meta_box_args Array of the metabox arguments
430
		 * @param mixed  $cmb           The CMB2 instance
431
		 */
432
		$show = (bool) apply_filters( 'cmb2_show_on', $show, $this->cmb->meta_box, $this->cmb );
433
434
		return $show;
435
	}
436
437
	/**
438
	 * Get the CMB priority property set to numeric hook priority.
439
	 * @since  2.2.0
440
	 * @param  integer $default Default display hook priority.
441
	 * @return integer          Hook priority.
442
	 */
443
	public function get_priority( $default = 10 ) {
444
		$priority = $this->cmb->prop( 'priority' );
445
446
		if ( ! is_numeric( $priority ) ) {
447
			switch ( $priority ) {
448
449
				case 'high':
450
					$priority = 5;
451
					break;
452
453
				case 'low':
454
					$priority = 20;
455
					break;
456
457
				default:
458
					$priority = $default;
459
					break;
460
			}
461
		}
462
463
		return $priority;
464
	}
465
466
	/**
467
	 * Save data from post metabox
468
	 * @since  1.0.0
469
	 * @param  int    $post_id Post ID
470
	 * @param  mixed  $post    Post object
471
	 * @return null
472
	 */
473
	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...
474
475
		$post_type = $post ? $post->post_type : get_post_type( $post_id );
476
477
		$do_not_pass_go = (
478
			! $this->can_save( $post_type )
479
			// check user editing permissions
480
			|| ( 'page' == $post_type && ! current_user_can( 'edit_page', $post_id ) )
481
			|| ! current_user_can( 'edit_post', $post_id )
482
		);
483
484
		if ( $do_not_pass_go ) {
485
			// do not collect $200
486
			return;
487
		}
488
489
		// take a trip to reading railroad – if you pass go collect $200
490
		$this->cmb->save_fields( $post_id, 'post', $_POST );
491
	}
492
493
	/**
494
	 * Save data from comment metabox
495
	 * @since  2.0.9
496
	 * @param  int    $comment_id Comment ID
497
	 * @return null
498
	 */
499
	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...
500
501
		$can_edit = current_user_can( 'moderate_comments', $comment_id );
502
503
		if ( $this->can_save( get_comment_type( $comment_id ) ) && $can_edit ) {
504
			$this->cmb->save_fields( $comment_id, 'comment', $_POST );
505
		}
506
	}
507
508
	/**
509
	 * Save data from user fields
510
	 * @since  1.0.x
511
	 * @param  int   $user_id  User ID
512
	 * @return null
513
	 */
514
	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...
515
		// check permissions
516
		if ( $this->can_save( 'user' ) ) {
517
			$this->cmb->save_fields( $user_id, 'user', $_POST );
518
		}
519
	}
520
521
	/**
522
	 * Save data from term fields
523
	 * @since  2.2.0
524
	 * @param  int    $term_id  Term ID
525
	 * @param  int    $tt_id    Term Taxonomy ID
526
	 * @param  string $taxonomy Taxonomy
527
	 * @return null
528
	 */
529
	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...
530
		$taxonomy = $taxonomy ? $taxonomy : $tt_id;
531
532
		// check permissions
533
		if ( $this->taxonomy_can_save( $taxonomy ) && $this->can_save( 'term' ) ) {
534
			$this->cmb->save_fields( $term_id, 'term', $_POST );
535
		}
536
	}
537
538
	/**
539
	 * Delete term meta when a term is deleted.
540
	 * @since  2.2.0
541
	 * @param  int    $term_id  Term ID
542
	 * @param  int    $tt_id    Term Taxonomy ID
543
	 * @param  string $taxonomy Taxonomy
544
	 * @return null
545
	 */
546
	public function delete_term( $term_id, $tt_id, $taxonomy = '' ) {
547
		if ( $this->taxonomy_can_save( $taxonomy ) ) {
548
549
			foreach ( $this->cmb->prop( 'fields' ) as $field ) {
550
				$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...
551
			}
552
553
			$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...
554
		}
555
	}
556
557
	/**
558
	 * Determines if the current object is able to be saved
559
	 * @since  2.0.9
560
	 * @param  string  $type Current post_type or comment_type
561
	 * @return bool          Whether object can be saved
562
	 */
563
	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...
564
		return (
565
			$this->cmb->prop( 'save_fields' )
566
			// check nonce
567
			&& isset( $_POST[ $this->cmb->nonce() ] )
568
			&& wp_verify_nonce( $_POST[ $this->cmb->nonce() ], $this->cmb->nonce() )
569
			// check if autosave
570
			&& ! ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
571
			// get the metabox types & compare it to this type
572
			&& ( $type && in_array( $type, $this->cmb->prop( 'object_types' ) ) )
573
		);
574
	}
575
576
	/**
577
	 * Determine if taxonomy of term being modified is cmb2-editable.
578
	 * @since  2.2.0
579
	 * @param  string $taxonomy Taxonomy of term being modified.
580
	 * @return bool             Whether taxonomy is editable.
581
	 */
582
	public function taxonomy_can_save( $taxonomy ) {
583
		if ( empty( $this->taxonomies ) || ! in_array( $taxonomy, $this->taxonomies ) ) {
584
			return false;
585
		}
586
587
		$taxonomy_object = get_taxonomy( $taxonomy );
588
		// Can the user edit this term?
589
		if ( ! isset( $taxonomy_object->cap ) || ! current_user_can( $taxonomy_object->cap->edit_terms ) ) {
590
			return false;
591
		}
592
593
		return true;
594
	}
595
596
	/**
597
	 * Enqueues the 'cmb2-display-styles' if the conditions match (has columns, on the right page, etc).
598
	 * @since  2.2.2.1
599
	 */
600
	protected function maybe_enqueue_column_display_styles() {
601
		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...
602
		if (
603
			$pagenow
604
			&& $this->cmb->has_columns
0 ignored issues
show
Documentation introduced by
The property $has_columns is declared protected in CMB2. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
605
			&& $this->cmb->prop( 'cmb_styles' )
606
			&& in_array( $pagenow, array( 'edit.php', 'users.php', 'edit-comments.php', 'edit-tags.php' ), 1 )
607
			) {
608
			self::enqueue_cmb_css( 'cmb2-display-styles' );
609
		}
610
	}
611
612
	/**
613
	 * Includes CMB2 styles
614
	 * @since  2.0.0
615
	 */
616 1
	public static function enqueue_cmb_css( $handle = 'cmb2-styles' ) {
617 1
		if ( ! apply_filters( 'cmb2_enqueue_css', true ) ) {
618
			return false;
619
		}
620
621 1
		self::register_styles();
622
623
		/*
624
		 * White list the options as this method can be used as a hook callback
625
		 * and have a different argument passed.
626
		 */
627 1
		return wp_enqueue_style( 'cmb2-display-styles' === $handle ? $handle : 'cmb2-styles' );
628
	}
629
630
	/**
631
	 * Includes CMB2 JS
632
	 * @since  2.0.0
633
	 */
634 1
	public static function enqueue_cmb_js() {
635 1
		if ( ! apply_filters( 'cmb2_enqueue_js', true ) ) {
636
			return false;
637
		}
638
639 1
		self::register_js();
640 1
		return true;
641
	}
642
643
}
644