Completed
Pull Request — trunk (#541)
by Justin
06:42
created

includes/CMB2_hookup.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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