Completed
Pull Request — master (#77)
by
unknown
02:25
created

Post_Meta_Container::hide_on_template()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 12
ccs 0
cts 0
cp 0
crap 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Carbon_Fields\Container;
4
5
use Carbon_Fields\Datastore\Meta_Datastore;
6
use Carbon_Fields\Datastore\Post_Meta_Datastore;
7
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
8
9
/**
10
 * Field container designed to extend WordPress custom fields functionality,
11
 * providing easier user interface to add, edit and delete text, media files,
12
 * location information and more.
13
 */
14
class Post_Meta_Container extends Container {
15
	/**
16
	 * ID of the post the container is working with
17
	 *
18
	 * @see init()
19
	 * @var int
20
	 */
21
	protected $post_id;
22
23
	/**
24
	 * List of default container settings
25
	 *
26
	 * @see init()
27
	 * @var array
28
	 */
29
	public $settings = array(
30
		'post_type' => array( 'post' ),
31
		'panel_context' => 'normal',
32
		'panel_priority' => 'high',
33
		'show_on' => array(
34
			'category' => null,
35
			'template_names' => array(),
36
			'not_in_template_names' => array(),
37
			'post_formats' => array(),
38
			'level_limit' => null,
39
			'tax_term_id' => null,
40
			'page_id' => null,
41
			'parent_page_id' => null,
42
			'post_path' => null,
43
		),
44
	);
45
46
	/**
47
	 * Create a new post meta fields container
48
	 *
49
	 * @param string $title Unique title of the container
50
	 **/
51
	public function __construct( $title ) {
52
		parent::__construct( $title );
53
54
		if ( ! $this->get_datastore() ) {
55
			$this->set_datastore( new Post_Meta_Datastore() );
56
		}
57
	}
58
59
	/**
60
	 * Check if all required container settings have been specified
61
	 *
62
	 * @param array $settings Container settings
63
	 **/
64
	public function check_setup_settings( &$settings = array() ) {
65
		if ( isset( $settings['show_on'] ) ) {
66
			$invalid_settings = array_diff_key( $settings['show_on'], $this->settings['show_on'] );
67
			if ( ! empty( $invalid_settings ) ) {
68
				Incorrect_Syntax_Exception::raise( 'Invalid show_on settings supplied to setup(): "' . implode( '", "', array_keys( $invalid_settings ) ) . '"' );
69
			}
70
		}
71
72
		if ( isset( $settings['show_on']['post_formats'] ) ) {
73
			$settings['show_on']['post_formats'] = (array) $settings['show_on']['post_formats'];
74
		}
75
76
		if ( isset( $settings['show_on']['post_path'] ) ) {
77
			$page = get_page_by_path( $settings['show_on']['post_path'] );
78
79
			if ( $page ) {
80
				$settings['show_on']['page_id'] = $page->ID;
81
			} else {
82
				$settings['show_on']['page_id'] = -1;
83
			}
84
		}
85
86
		// Transform category slug to taxonomy + term slug + term id
87
		if ( isset( $settings['show_on']['category'] ) ) {
88
			$term = get_term_by( 'slug', $settings['show_on']['category'], 'category' );
89
90
			if ( $term ) {
91
				$settings['show_on']['tax_slug'] = $term->taxonomy;
92
				$settings['show_on']['tax_term'] = $term->slug;
93
				$settings['show_on']['tax_term_id'] = $term->term_id;
94
			}
95
		}
96
97
		return parent::check_setup_settings( $settings );
98
	}
99
100
	/**
101
	 * Create DataStore instance, set post ID to operate with (if such exists).
102
	 * Bind attach() and save() to the appropriate WordPress actions.
103
	 **/
104
	public function init() {
105
		if ( isset( $_GET['post'] ) ) {
1 ignored issue
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
106
			$this->set_post_id( $_GET['post'] );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
107
		}
108
109
		// force post_type to be array
110
		if ( ! is_array( $this->settings['post_type'] ) ) {
111
			$this->settings['post_type'] = array( $this->settings['post_type'] );
112
		}
113
114
		add_action( 'admin_init', array( $this, '_attach' ) );
115
		add_action( 'save_post', array( $this, '_save' ) );
116
117
		// support for attachments
118
		add_action( 'add_attachment', array( $this, '_save' ) );
119
		add_action( 'edit_attachment', array( $this, '_save' ) );
120
	}
121
122
	/**
123
	 * Perform save operation after successful is_valid_save() check.
124
	 * The call is propagated to all fields in the container.
125
	 *
126
	 * @param int $post_id ID of the post against which save() is ran
127
	 **/
128 View Code Duplication
	public function save( $post_id ) {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129
		// Unhook action to garantee single save
130
		remove_action( 'save_post', array( $this, '_save' ) );
131
132
		$this->set_post_id( $post_id );
133
134
		foreach ( $this->fields as $field ) {
135
			$field->set_value_from_input();
136
			$field->save();
137
		}
138
139
		do_action( 'carbon_after_save_custom_fields', $post_id );
140
		do_action( 'carbon_after_save_post_meta', $post_id );
141
	}
142
143
	/**
144
	 * Perform checks whether the current save() request is valid
145
	 * Possible errors are triggering save() for autosave requests
146
	 * or performing post save outside of the post edit page (like Quick Edit)
147
	 *
148
	 * @see is_valid_save_conditions()
149
	 * @param int $post_id ID of the post against which save() is ran
150
	 * @return bool
151
	 **/
152
	public function is_valid_save( $post_id = 0 ) {
153
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
154
			return false;
155
		} else if ( ! isset( $_REQUEST[ $this->get_nonce_name() ] ) || ! wp_verify_nonce( $_REQUEST[ $this->get_nonce_name() ], $this->get_nonce_name() ) ) { // Input var okay.
0 ignored issues
show
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
156
			return false;
157
		} else if ( $post_id < 1 ) {
158
			return false;
159
		}
160
161
		return $this->is_valid_save_conditions( $post_id );
162
	}
163
164
	/**
165
	 * Perform checks whether the current save() request is valid
166
	 * Possible errors are triggering save() for autosave requests
167
	 * or performing post save outside of the post edit page (like Quick Edit)
168
	 *
169
	 * @param int $post_id ID of the post against which save() is ran
170
	 * @return bool
171
	 **/
172
	public function is_valid_save_conditions( $post_id ) {
173
		$valid = true;
174
		$post = get_post( $post_id );
175
176
		// Check post type
177
		if ( ! in_array( $post->post_type, $this->settings['post_type'] ) ) {
178
			return false;
179
		}
180
181
		// Check show on conditions
182
		foreach ( $this->settings['show_on'] as $condition => $value ) {
183
			if ( is_null( $value ) ) {
184
				continue;
185
			}
186
187
			switch ( $condition ) {
188
				// show_on_post_format
189
				case 'post_formats':
190
					if ( empty( $value ) || $post->post_type != 'post' ) {
191
						break;
192
					}
193
194
					$current_format = get_post_format( $post_id );
195
					if ( ! in_array( $current_format, $value ) ) {
196
						$valid = false;
197
						break 2;
198
					}
199
200
					break;
201
202
				// show_on_taxonomy_term or show_on_category
203
				case 'category':
204
					$this->show_on_category( $value );
205
206
					/* fall-through intended */
207
				case 'tax_term_id':
208
					$current_terms = wp_get_object_terms( $post_id, $this->settings['show_on']['tax_slug'], array( 'fields' => 'ids' ) );
209
210
					if ( ! is_array( $current_terms ) || ! in_array( $this->settings['show_on']['tax_term_id'], $current_terms ) ) {
211
						$valid = false;
212
						break 2;
213
					}
214
215
					break;
216
217
				// show_on_level
218
				case 'level_limit':
219
					$post_level = count( get_post_ancestors( $post_id ) ) + 1;
220
221
					if ( $post_level != $value ) {
222
						$valid = false;
223
						break 2;
224
					}
225
226
					break;
227
228
				// show_on_page
229
				case 'page_id':
230
					if ( $post_id != $value ) {
231
						$valid = false;
232
						break 2;
233
					}
234
235
					break;
236
237
				// show_on_page_children
238
				case 'parent_page_id':
239
					if ( $post->post_parent != $value ) {
240
						$valid = false;
241
						break 2;
242
					}
243
244
					break;
245
246
				// show_on_template
247 View Code Duplication
				case 'template_names':
1 ignored issue
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...
248
					if ( empty( $value ) || $post->post_type != 'page' ) {
249
						break;
250
					}
251
					$current_template = get_post_meta( $post_id, '_wp_page_template', 1 );
252
253
					if ( ! in_array( $current_template, $value ) ) {
254
						$valid = false;
255
						break 2;
256
					}
257
258
					break;
259
260
				// hide_on_template
261 View Code Duplication
				case 'not_in_template_names':
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...
262
					if ( empty( $value ) || $post->post_type != 'page' ) {
263
						break;
264
					}
265
					$current_template = get_post_meta( $post_id, '_wp_page_template', 1 );
266
267
					if ( in_array( $current_template, $value ) ) {
268
						$valid = false;
269
						break 2;
270
					}
271
272
					break;
273
			}
274
		}
275
276
		return $valid;
277
	}
278
279
	/**
280
	 * Add meta box for each of the container post types
281
	 **/
282
	public function attach() {
283
		foreach ( $this->settings['post_type'] as $post_type ) {
284
			add_meta_box(
285
				$this->id,
286
				$this->title,
287
				array( $this, 'render' ),
288
				$post_type,
289
				$this->settings['panel_context'],
290
				$this->settings['panel_priority']
291
			);
292
		}
293
294
		foreach ( $this->settings['post_type'] as $post_type ) {
295
			add_filter( "postbox_classes_{$post_type}_{$this->id}", array( $this, 'postbox_classes' ) );
296
		}
297
	}
298
299
	/**
300
	 * Classes to add to the post meta box
301
	 */
302
	public function postbox_classes( $classes ) {
303
		$classes[] = 'carbon-box';
304
		return $classes;
305
	}
306
307
	/**
308
	 * Perform checks whether the container should be attached during the current request
309
	 *
310
	 * @return bool True if the container is allowed to be attached
311
	 **/
312
	public function is_valid_attach() {
313
		global $pagenow;
314
315
		if ( $pagenow !== 'post.php' && $pagenow !== 'post-new.php' ) {
316
			return false;
317
		}
318
319
		// Post types check
320
		if ( ! empty( $this->settings['post_type'] ) ) {
321
			$post_type = '';
322
323
			if ( $this->post_id ) {
324
				$post_type = get_post_type( $this->post_id );
325
			} elseif ( ! empty( $_GET['post_type'] ) ) {
326
				$post_type = $_GET['post_type'];
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
327
			} elseif ( $pagenow === 'post-new.php' ) {
328
				$post_type = 'post';
329
			}
330
331
			if ( ! $post_type || ! in_array( $post_type, $this->settings['post_type'] ) ) {
332
				return false;
333
			}
334
		}
335
336
		// Check show on conditions
337
		foreach ( $this->settings['show_on'] as $condition => $value ) {
338
			if ( is_null( $value ) ) {
339
				continue;
340
			}
341
342
			switch ( $condition ) {
343
				case 'page_id':
344
					if ( $value < 1 || $this->post_id != $value ) {
345
						return false;
346
					}
347
					break;
348
				case 'parent_page_id':
349
					// Check if such page exists
350
					if ( $value < 1 ) {
351
						return false;
352
					}
353
					break;
354
			}
355
		}
356
357
		return true;
358
	}
359
360
	/**
361
	 * Revert the result of attach()
362
	 **/
363 View Code Duplication
	public function detach() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
364
		parent::detach();
365
366
		remove_action( 'admin_init', array( $this, '_attach' ) );
367
		remove_action( 'save_post', array( $this, '_save' ) );
368
369
		// unregister field names
370
		foreach ( $this->fields as $field ) {
371
			$this->drop_unique_field_name( $field->get_name() );
372
		}
373
	}
374
375
	/**
376
	 * Output the container markup
377
	 **/
378
	public function render() {
379
		include \Carbon_Fields\DIR . '/templates/Container/post_meta.php';
380
	}
381
382
	/**
383
	 * Set the post ID the container will operate with.
384
	 *
385
	 * @param int $post_id
386
	 **/
387
	public function set_post_id( $post_id ) {
388
		$this->post_id = $post_id;
389
		$this->store->set_id( $post_id );
390
	}
391
392
	/**
393
	 * Show the container only on pages whose parent is referenced by $parent_page_path.
394
	 *
395
	 * @param string $parent_page_path
396
	 * @return object $this
397
	 **/
398 1
	public function show_on_page_children( $parent_page_path ) {
399
		$page = get_page_by_path( $parent_page_path );
400
401
		$this->show_on_post_type( 'page' );
402
403 1
		if ( $page ) {
404 1
			$this->settings['show_on']['parent_page_id'] = $page->ID;
405
		} else {
406
			$this->settings['show_on']['parent_page_id'] = -1;
407 1
		}
408
409 1
		return $this;
410
	}
411
412
	/**
413
	 * Show the container only on particular page referenced by it's path.
414
	 *
415
	 * @param int|string $page page ID or page path
416
	 * @return object $this
417
	 **/
418 4
	public function show_on_page( $page ) {
419
		$page_id = absint( $page );
420
421 4
		if ( $page_id && $page_id == $page ) {
422
			$page_obj = get_post( $page_id );
423
		} else {
424
			$page_obj = get_page_by_path( $page );
425 2
		}
426
427
		$this->show_on_post_type( 'page' );
428
429 4
		if ( $page_obj ) {
430 4
			$this->settings['show_on']['page_id'] = $page_obj->ID;
431
		} else {
432
			$this->settings['show_on']['page_id'] = -1;
433 4
		}
434
435 4
		return $this;
436
	}
437
438
	/**
439
	 * Show the container only on posts from the specified category.
440
	 *
441
	 * @see show_on_taxonomy_term()
442
	 *
443
	 * @param string $category_slug
444
	 * @return object $this
445
	 **/
446
	public function show_on_category( $category_slug ) {
447
		$this->settings['show_on']['category'] = $category_slug;
448
449
		return $this->show_on_taxonomy_term( $category_slug, 'category' );
450
	}
451
452
	/**
453
	 * Show the container only on pages whose template has filename $template_path.
454
	 *
455
	 * @param string|array $template_path
456
	 * @return object $this
457
	 **/
458 2
	public function show_on_template( $template_path ) {
459
		$this->show_on_post_type( 'page' );
460
461
		if ( is_array( $template_path ) ) {
462
			foreach ( $template_path as $path ) {
463
				$this->show_on_template( $path );
464
			}
465
466 1
			return $this;
467
		}
468
469 2
		$this->settings['show_on']['template_names'][] = $template_path;
470
471 2
		return $this;
472
	}
473
474
	/**
475
	 * Hide the container from pages whose template has filename $template_path.
476
	 *
477
	 * @param string|array $template_path
478
	 * @return object $this
479
	 **/
480
	public function hide_on_template( $template_path ) {
481
		if ( is_array( $template_path ) ) {
482
			foreach ( $template_path as $path ) {
483
				$this->hide_on_template( $path );
484
			}
485
			return $this;
486
		}
487
488
		$this->settings['show_on']['not_in_template_names'][] = $template_path;
489
490
		return $this;
491
	}
492
493
	/**
494
	 * Show the container only on hierarchical posts of level $level.
495
	 * Levels start from 1 (top level post)
496
	 *
497
	 * @param int $level
498
	 * @return object $this
499
	 **/
500
	public function show_on_level( $level ) {
501
		if ( $level < 0 ) {
502
			Incorrect_Syntax_Exception::raise( 'Invalid level limitation (' . $level . ')' );
503
		}
504
505
		$this->settings['show_on']['level_limit'] = $level;
506
507
		return $this;
508
	}
509
510
	/**
511
	 * Show the container only on posts which have term $term_slug from the $taxonomy_slug taxonomy.
512
	 *
513
	 * @param string $taxonomy_slug
514
	 * @param string $term_slug
515
	 * @return object $this
516
	 **/
517
	public function show_on_taxonomy_term( $term_slug, $taxonomy_slug ) {
518
		$term = get_term_by( 'slug', $term_slug, $taxonomy_slug );
519
520
		$this->settings['show_on']['tax_slug'] = $taxonomy_slug;
521
		$this->settings['show_on']['tax_term'] = $term_slug;
522
		$this->settings['show_on']['tax_term_id'] = $term ? $term->term_id : null;
523
524
		return $this;
525
	}
526
527
	/**
528
	 * Show the container only on posts from the specified format.
529
	 * Learn more about {@link http://codex.wordpress.org/Post_Formats Post Formats (Codex)}
530
	 *
531
	 * @param string|array $post_format Name of the format as listed on Codex
532
	 * @return object $this
533
	 **/
534
	public function show_on_post_format( $post_format ) {
535
		if ( is_array( $post_format ) ) {
536
			foreach ( $post_format as $format ) {
537
				$this->show_on_post_format( $format );
538
			}
539
			return $this;
540
		}
541
542
		if ( $post_format === 'standard' ) {
543
			$post_format = 0;
544
		}
545
546
		$this->settings['show_on']['post_formats'][] = strtolower( $post_format );
547
548
		return $this;
549
	}
550
551
	/**
552
	 * Show the container only on posts from the specified type(s).
553
	 *
554
	 * @param string|array $post_types
555
	 * @return object $this
556
	 **/
557
	public function show_on_post_type( $post_types ) {
558
		$post_types = (array) $post_types;
559
560
		$this->settings['post_type'] = $post_types;
561
562
		return $this;
563
	}
564
565
	/**
566
	 * Sets the meta box container context
567
	 *
568
	 * @see https://codex.wordpress.org/Function_Reference/add_meta_box
569
	 * @param string $context ('normal', 'advanced' or 'side')
570
	 */
571
	public function set_context( $context ) {
572
		$this->settings['panel_context'] = $context;
573
574
		return $this;
575
	}
576
577
	/**
578
	 * Sets the meta box container priority
579
	 *
580
	 * @see https://codex.wordpress.org/Function_Reference/add_meta_box
581
	 * @param string $priority ('high', 'core', 'default' or 'low')
582
	 */
583
	public function set_priority( $priority ) {
584
		$this->settings['panel_priority'] = $priority;
585
586
		return $this;
587
	}
588
} // END Post_Meta_Container
589