Completed
Push — master ( 3437e0...7a4f07 )
by
unknown
02:43
created

Post_Meta_Container::set_context()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 3
cp 0
crap 2
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 1
		$page = get_page_by_path( $parent_page_path );
400
401 1
		$this->show_on_post_type( 'page' );
402
403 1
		if ( $page ) {
404 1
			$this->settings['show_on']['parent_page_id'] = $page->ID;
405 1
		} else {
406
			$this->settings['show_on']['parent_page_id'] = -1;
407
		}
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 4
		$page_id = absint( $page );
420
421 4
		if ( $page_id && $page_id == $page ) {
422 2
			$page_obj = get_post( $page_id );
423 2
		} else {
424 2
			$page_obj = get_page_by_path( $page );
425
		}
426
427 4
		$this->show_on_post_type( 'page' );
428
429 4
		if ( $page_obj ) {
430 4
			$this->settings['show_on']['page_id'] = $page_obj->ID;
431 4
		} else {
432
			$this->settings['show_on']['page_id'] = -1;
433
		}
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
		// Backwards compatibility where only pages support templates
460 2
		if ( version_compare( get_bloginfo( 'version' ), '4.7', '<' ) ) {
461 2
			$this->show_on_post_type( 'page' );
462 2
		}
463
464 2
		if ( is_array( $template_path ) ) {
465 1
			foreach ( $template_path as $path ) {
466 1
				$this->show_on_template( $path );
467 1
			}
468
469 1
			return $this;
470
		}
471
472 2
		$this->settings['show_on']['template_names'][] = $template_path;
473
474 2
		return $this;
475
	}
476
477
	/**
478
	 * Hide the container from pages whose template has filename $template_path.
479
	 *
480
	 * @param string|array $template_path
481
	 * @return object $this
482
	 **/
483
	public function hide_on_template( $template_path ) {
484
		if ( is_array( $template_path ) ) {
485
			foreach ( $template_path as $path ) {
486
				$this->hide_on_template( $path );
487
			}
488
			return $this;
489
		}
490
491
		$this->settings['show_on']['not_in_template_names'][] = $template_path;
492
493
		return $this;
494
	}
495
496
	/**
497
	 * Show the container only on hierarchical posts of level $level.
498
	 * Levels start from 1 (top level post)
499
	 *
500
	 * @param int $level
501
	 * @return object $this
502
	 **/
503
	public function show_on_level( $level ) {
504
		if ( $level < 0 ) {
505
			Incorrect_Syntax_Exception::raise( 'Invalid level limitation (' . $level . ')' );
506
		}
507
508
		$this->settings['show_on']['level_limit'] = $level;
509
510
		return $this;
511
	}
512
513
	/**
514
	 * Show the container only on posts which have term $term_slug from the $taxonomy_slug taxonomy.
515
	 *
516
	 * @param string $taxonomy_slug
517
	 * @param string $term_slug
518
	 * @return object $this
519
	 **/
520
	public function show_on_taxonomy_term( $term_slug, $taxonomy_slug ) {
521
		$term = get_term_by( 'slug', $term_slug, $taxonomy_slug );
522
523
		$this->settings['show_on']['tax_slug'] = $taxonomy_slug;
524
		$this->settings['show_on']['tax_term'] = $term_slug;
525
		$this->settings['show_on']['tax_term_id'] = $term ? $term->term_id : null;
526
527
		return $this;
528
	}
529
530
	/**
531
	 * Show the container only on posts from the specified format.
532
	 * Learn more about {@link http://codex.wordpress.org/Post_Formats Post Formats (Codex)}
533
	 *
534
	 * @param string|array $post_format Name of the format as listed on Codex
535
	 * @return object $this
536
	 **/
537
	public function show_on_post_format( $post_format ) {
538
		if ( is_array( $post_format ) ) {
539
			foreach ( $post_format as $format ) {
540
				$this->show_on_post_format( $format );
541
			}
542
			return $this;
543
		}
544
545
		if ( $post_format === 'standard' ) {
546
			$post_format = 0;
547
		}
548
549
		$this->settings['show_on']['post_formats'][] = strtolower( $post_format );
550
551
		return $this;
552
	}
553
554
	/**
555
	 * Show the container only on posts from the specified type(s).
556
	 *
557
	 * @param string|array $post_types
558
	 * @return object $this
559
	 **/
560
	public function show_on_post_type( $post_types ) {
561
		$post_types = (array) $post_types;
562
563
		$this->settings['post_type'] = $post_types;
564
565
		return $this;
566
	}
567
568
	/**
569
	 * Sets the meta box container context
570
	 *
571
	 * @see https://codex.wordpress.org/Function_Reference/add_meta_box
572
	 * @param string $context ('normal', 'advanced' or 'side')
573
	 */
574
	public function set_context( $context ) {
575
		$this->settings['panel_context'] = $context;
576
577
		return $this;
578
	}
579
580
	/**
581
	 * Sets the meta box container priority
582
	 *
583
	 * @see https://codex.wordpress.org/Function_Reference/add_meta_box
584
	 * @param string $priority ('high', 'core', 'default' or 'low')
585
	 */
586
	public function set_priority( $priority ) {
587
		$this->settings['panel_priority'] = $priority;
588
589
		return $this;
590
	}
591
} // END Post_Meta_Container
592