Wordpress 4.6
[autoinstalls/wordpress.git] / wp-includes / class-wp-post-type.php
1 <?php
2 /**
3  * Post API: WP_Post_Type class
4  *
5  * @package WordPress
6  * @subpackage Post
7  * @since 4.6.0
8  */
9
10 /**
11  * Core class used for interacting with post types.
12  *
13  * @since 4.6.0
14  *
15  * @see register_post_type()
16  */
17 final class WP_Post_Type {
18         /**
19          * Post type key.
20          *
21          * @since 4.6.0
22          * @access public
23          * @var string $name
24          */
25         public $name;
26
27         /**
28          * Name of the post type shown in the menu. Usually plural.
29          *
30          * @since 4.6.0
31          * @access public
32          * @var string $label
33          */
34         public $label;
35
36         /**
37          * Labels object for this post type.
38          *
39          * If not set, post labels are inherited for non-hierarchical types
40          * and page labels for hierarchical ones.
41          *
42          * @see get_post_type_labels()
43          *
44          * @since 4.6.0
45          * @access public
46          * @var object $labels
47          */
48         public $labels;
49
50         /**
51          * A short descriptive summary of what the post type is.
52          *
53          * Default empty.
54          *
55          * @since 4.6.0
56          * @access public
57          * @var string $description
58          */
59         public $description = '';
60
61         /**
62          * Whether a post type is intended for use publicly either via the admin interface or by front-end users.
63          *
64          * While the default settings of $exclude_from_search, $publicly_queryable, $show_ui, and $show_in_nav_menus
65          * are inherited from public, each does not rely on this relationship and controls a very specific intention.
66          *
67          * Default false.
68          *
69          * @since 4.6.0
70          * @access public
71          * @var bool $public
72          */
73         public $public = false;
74
75         /**
76          * Whether the post type is hierarchical (e.g. page).
77          *
78          * Default false.
79          *
80          * @since 4.6.0
81          * @access public
82          * @var bool $hierarchical
83          */
84         public $hierarchical = false;
85
86         /**
87          * Whether to exclude posts with this post type from front end search
88          * results.
89          *
90          * Default is the opposite value of $public.
91          *
92          * @since 4.6.0
93          * @access public
94          * @var bool $exclude_from_search
95          */
96         public $exclude_from_search = null;
97
98         /**
99          * Whether queries can be performed on the front end for the post type as part of `parse_request()`.
100          *
101          * Endpoints would include:
102          * - `?post_type={post_type_key}`
103          * - `?{post_type_key}={single_post_slug}`
104          * - `?{post_type_query_var}={single_post_slug}`
105          *
106          * Default is the value of $public.
107          *
108          * @since 4.6.0
109          * @access public
110          * @var bool $publicly_queryable
111          */
112         public $publicly_queryable = null;
113
114         /**
115          * Whether to generate and allow a UI for managing this post type in the admin.
116          *
117          * Default is the value of $public.
118          *
119          * @since 4.6.0
120          * @access public
121          * @var bool $show_ui
122          */
123         public $show_ui = null;
124
125         /**
126          * Where to show the post type in the admin menu.
127          *
128          * To work, $show_ui must be true. If true, the post type is shown in its own top level menu. If false, no menu is
129          * shown. If a string of an existing top level menu (eg. 'tools.php' or 'edit.php?post_type=page'), the post type
130          * will be placed as a sub-menu of that.
131          *
132          * Default is the value of $show_ui.
133          *
134          * @since 4.6.0
135          * @access public
136          * @var bool $show_in_menu
137          */
138         public $show_in_menu = null;
139
140         /**
141          * Makes this post type available for selection in navigation menus.
142          *
143          * Default is the value $public.
144          *
145          * @since 4.6.0
146          * @access public
147          * @var bool $show_in_nav_menus
148          */
149         public $show_in_nav_menus = null;
150
151         /**
152          * Makes this post type available via the admin bar.
153          *
154          * Default is the value of $show_in_menu.
155          *
156          * @since 4.6.0
157          * @access public
158          * @var bool $show_in_admin_bar
159          */
160         public $show_in_admin_bar = null;
161
162         /**
163          * The position in the menu order the post type should appear.
164          *
165          * To work, $show_in_menu must be true. Default null (at the bottom).
166          *
167          * @since 4.6.0
168          * @access public
169          * @var int $menu_position
170          */
171         public $menu_position = null;
172
173         /**
174          * The URL to the icon to be used for this menu.
175          *
176          * Pass a base64-encoded SVG using a data URI, which will be colored to match the color scheme.
177          * This should begin with 'data:image/svg+xml;base64,'. Pass the name of a Dashicons helper class
178          * to use a font icon, e.g. 'dashicons-chart-pie'. Pass 'none' to leave div.wp-menu-image empty
179          * so an icon can be added via CSS.
180          *
181          * Defaults to use the posts icon.
182          *
183          * @since 4.6.0
184          * @access public
185          * @var string $menu_icon
186          */
187         public $menu_icon = null;
188
189         /**
190          * The string to use to build the read, edit, and delete capabilities.
191          *
192          * May be passed as an array to allow for alternative plurals when using
193          * this argument as a base to construct the capabilities, e.g.
194          * array( 'story', 'stories' ). Default 'post'.
195          *
196          * @since 4.6.0
197          * @access public
198          * @var string $capability_type
199          */
200         public $capability_type = 'post';
201
202         /**
203          * Whether to use the internal default meta capability handling.
204          *
205          * Default false.
206          *
207          * @since 4.6.0
208          * @access public
209          * @var bool $map_meta_cap
210          */
211         public $map_meta_cap = false;
212
213         /**
214          * Provide a callback function that sets up the meta boxes for the edit form.
215          *
216          * Do `remove_meta_box()` and `add_meta_box()` calls in the callback. Default null.
217          *
218          * @since 4.6.0
219          * @access public
220          * @var string $register_meta_box_cb
221          */
222         public $register_meta_box_cb = null;
223
224         /**
225          * An array of taxonomy identifiers that will be registered for the post type.
226          *
227          * Taxonomies can be registered later with `register_taxonomy()` or `register_taxonomy_for_object_type()`.
228          *
229          * Default empty array.
230          *
231          * @since 4.6.0
232          * @access public
233          * @var array $taxonomies
234          */
235         public $taxonomies = array();
236
237         /**
238          * Whether there should be post type archives, or if a string, the archive slug to use.
239          *
240          * Will generate the proper rewrite rules if $rewrite is enabled. Default false.
241          *
242          * @since 4.6.0
243          * @access public
244          * @var bool|string $has_archive
245          */
246         public $has_archive = false;
247
248         /**
249          * Sets the query_var key for this post type.
250          *
251          * Defaults to $post_type key. If false, a post type cannot be loaded at `?{query_var}={post_slug}`.
252          * If specified as a string, the query `?{query_var_string}={post_slug}` will be valid.
253          *
254          * @since 4.6.0
255          * @access public
256          * @var string|bool $query_var
257          */
258         public $query_var;
259
260         /**
261          * Whether to allow this post type to be exported.
262          *
263          * Default true.
264          *
265          * @since 4.6.0
266          * @access public
267          * @var bool $can_export
268          */
269         public $can_export = true;
270
271         /**
272          * Whether to delete posts of this type when deleting a user.
273          *
274          * If true, posts of this type belonging to the user will be moved to trash when then user is deleted.
275          * If false, posts of this type belonging to the user will *not* be trashed or deleted.
276          * If not set (the default), posts are trashed if post_type_supports( 'author' ).
277          * Otherwise posts are not trashed or deleted. Default null.
278          *
279          * @since 4.6.0
280          * @access public
281          * @var bool $delete_with_user
282          */
283         public $delete_with_user = null;
284
285         /**
286          * Whether this post type is a native or "built-in" post_type.
287          *
288          * Default false.
289          *
290          * @since 4.6.0
291          * @access public
292          * @var bool $_builtin
293          */
294         public $_builtin = false;
295
296         /**
297          * URL segment to use for edit link of this post type.
298          *
299          * Default 'post.php?post=%d'.
300          *
301          * @since 4.6.0
302          * @access public
303          * @var string $_edit_link
304          */
305         public $_edit_link = 'post.php?post=%d';
306
307         /**
308          * Post type capabilities.
309          *
310          * @since 4.6.0
311          * @access public
312          * @var object $cap
313          */
314         public $cap;
315
316         /**
317          * Triggers the handling of rewrites for this post type.
318          *
319          * Defaults to true, using $post_type as slug.
320          *
321          * @since 4.6.0
322          * @access public
323          * @var array|false $rewrite
324          */
325         public $rewrite;
326
327         /**
328          * The features supported by the post type.
329          *
330          * @since 4.6.0
331          * @access public
332          * @var array|bool $supports
333          */
334         public $supports;
335
336         /**
337          * Constructor.
338          *
339          * Will populate object properties from the provided arguments and assign other
340          * default properties based on that information.
341          *
342          * @since 4.6.0
343          * @access public
344          *
345          * @see register_post_type()
346          *
347          * @param string       $post_type Post type key.
348          * @param array|string $args      Optional. Array or string of arguments for registering a post type.
349          *                                Default empty array.
350          */
351         public function __construct( $post_type, $args = array() ) {
352                 $this->name = $post_type;
353
354                 $this->set_props( $args );
355         }
356
357         /**
358          * Sets post type properties.
359          *
360          * @since 4.6.0
361          * @access public
362          *
363          * @param array|string $args Array or string of arguments for registering a post type.
364          */
365         public function set_props( $args ) {
366                 $args = wp_parse_args( $args );
367
368                 /**
369                  * Filter the arguments for registering a post type.
370                  *
371                  * @since 4.4.0
372                  *
373                  * @param array  $args      Array of arguments for registering a post type.
374                  * @param string $post_type Post type key.
375                  */
376                 $args = apply_filters( 'register_post_type_args', $args, $this->name );
377
378                 $has_edit_link = ! empty( $args['_edit_link'] );
379
380                 // Args prefixed with an underscore are reserved for internal use.
381                 $defaults = array(
382                         'labels'               => array(),
383                         'description'          => '',
384                         'public'               => false,
385                         'hierarchical'         => false,
386                         'exclude_from_search'  => null,
387                         'publicly_queryable'   => null,
388                         'show_ui'              => null,
389                         'show_in_menu'         => null,
390                         'show_in_nav_menus'    => null,
391                         'show_in_admin_bar'    => null,
392                         'menu_position'        => null,
393                         'menu_icon'            => null,
394                         'capability_type'      => 'post',
395                         'capabilities'         => array(),
396                         'map_meta_cap'         => null,
397                         'supports'             => array(),
398                         'register_meta_box_cb' => null,
399                         'taxonomies'           => array(),
400                         'has_archive'          => false,
401                         'rewrite'              => true,
402                         'query_var'            => true,
403                         'can_export'           => true,
404                         'delete_with_user'     => null,
405                         '_builtin'             => false,
406                         '_edit_link'           => 'post.php?post=%d',
407                 );
408
409                 $args = array_merge( $defaults, $args );
410
411                 $args['name'] = $this->name;
412
413                 // If not set, default to the setting for public.
414                 if ( null === $args['publicly_queryable'] ) {
415                         $args['publicly_queryable'] = $args['public'];
416                 }
417
418                 // If not set, default to the setting for public.
419                 if ( null === $args['show_ui'] ) {
420                         $args['show_ui'] = $args['public'];
421                 }
422
423                 // If not set, default to the setting for show_ui.
424                 if ( null === $args['show_in_menu'] || ! $args['show_ui'] ) {
425                         $args['show_in_menu'] = $args['show_ui'];
426                 }
427
428                 // If not set, default to the whether the full UI is shown.
429                 if ( null === $args['show_in_admin_bar'] ) {
430                         $args['show_in_admin_bar'] = (bool) $args['show_in_menu'];
431                 }
432
433                 // If not set, default to the setting for public.
434                 if ( null === $args['show_in_nav_menus'] ) {
435                         $args['show_in_nav_menus'] = $args['public'];
436                 }
437
438                 // If not set, default to true if not public, false if public.
439                 if ( null === $args['exclude_from_search'] ) {
440                         $args['exclude_from_search'] = ! $args['public'];
441                 }
442
443                 // Back compat with quirky handling in version 3.0. #14122.
444                 if ( empty( $args['capabilities'] ) && null === $args['map_meta_cap'] && in_array( $args['capability_type'], array( 'post', 'page' ) ) ) {
445                         $args['map_meta_cap'] = true;
446                 }
447
448                 // If not set, default to false.
449                 if ( null === $args['map_meta_cap'] ) {
450                         $args['map_meta_cap'] = false;
451                 }
452
453                 // If there's no specified edit link and no UI, remove the edit link.
454                 if ( ! $args['show_ui'] && ! $has_edit_link ) {
455                         $args['_edit_link'] = '';
456                 }
457
458                 $this->cap = get_post_type_capabilities( (object) $args );
459                 unset( $args['capabilities'] );
460
461                 if ( is_array( $args['capability_type'] ) ) {
462                         $args['capability_type'] = $args['capability_type'][0];
463                 }
464
465                 if ( false !== $args['query_var'] ) {
466                         if ( true === $args['query_var'] ) {
467                                 $args['query_var'] = $this->name;
468                         } else {
469                                 $args['query_var'] = sanitize_title_with_dashes( $args['query_var'] );
470                         }
471                 }
472
473                 if ( false !== $args['rewrite'] && ( is_admin() || '' != get_option( 'permalink_structure' ) ) ) {
474                         if ( ! is_array( $args['rewrite'] ) ) {
475                                 $args['rewrite'] = array();
476                         }
477                         if ( empty( $args['rewrite']['slug'] ) ) {
478                                 $args['rewrite']['slug'] = $this->name;
479                         }
480                         if ( ! isset( $args['rewrite']['with_front'] ) ) {
481                                 $args['rewrite']['with_front'] = true;
482                         }
483                         if ( ! isset( $args['rewrite']['pages'] ) ) {
484                                 $args['rewrite']['pages'] = true;
485                         }
486                         if ( ! isset( $args['rewrite']['feeds'] ) || ! $args['has_archive'] ) {
487                                 $args['rewrite']['feeds'] = (bool) $args['has_archive'];
488                         }
489                         if ( ! isset( $args['rewrite']['ep_mask'] ) ) {
490                                 if ( isset( $args['permalink_epmask'] ) ) {
491                                         $args['rewrite']['ep_mask'] = $args['permalink_epmask'];
492                                 } else {
493                                         $args['rewrite']['ep_mask'] = EP_PERMALINK;
494                                 }
495                         }
496                 }
497
498                 foreach ( $args as $property_name => $property_value ) {
499                         $this->$property_name = $property_value;
500                 }
501
502                 $this->labels = get_post_type_labels( $this );
503                 $this->label  = $this->labels->name;
504         }
505
506         /**
507          * Sets the features support for the post type.
508          *
509          * @since 4.6.0
510          * @access public
511          */
512         public function add_supports() {
513                 if ( ! empty( $this->supports ) ) {
514                         add_post_type_support( $this->name, $this->supports );
515                         unset( $this->supports );
516                 } elseif ( false !== $this->supports ) {
517                         // Add default features.
518                         add_post_type_support( $this->name, array( 'title', 'editor' ) );
519                 }
520         }
521
522         /**
523          * Adds the necessary rewrite rules for the post type.
524          *
525          * @since 4.6.0
526          * @access public
527          *
528          * @global WP_Rewrite $wp_rewrite WordPress Rewrite Component.
529          * @global WP         $wp         Current WordPress environment instance.
530          */
531         public function add_rewrite_rules() {
532                 global $wp_rewrite, $wp;
533
534                 if ( false !== $this->query_var && $wp && is_post_type_viewable( $this ) ) {
535                         $wp->add_query_var( $this->query_var );
536                 }
537
538                 if ( false !== $this->rewrite && ( is_admin() || '' != get_option( 'permalink_structure' ) ) ) {
539                         if ( $this->hierarchical ) {
540                                 add_rewrite_tag( "%$this->name%", '(.+?)', $this->query_var ? "{$this->query_var}=" : "post_type=$this->name&pagename=" );
541                         } else {
542                                 add_rewrite_tag( "%$this->name%", '([^/]+)', $this->query_var ? "{$this->query_var}=" : "post_type=$this->name&name=" );
543                         }
544
545                         if ( $this->has_archive ) {
546                                 $archive_slug = true === $this->has_archive ? $this->rewrite['slug'] : $this->has_archive;
547                                 if ( $this->rewrite['with_front'] ) {
548                                         $archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug;
549                                 } else {
550                                         $archive_slug = $wp_rewrite->root . $archive_slug;
551                                 }
552
553                                 add_rewrite_rule( "{$archive_slug}/?$", "index.php?post_type=$this->name", 'top' );
554                                 if ( $this->rewrite['feeds'] && $wp_rewrite->feeds ) {
555                                         $feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
556                                         add_rewrite_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$this->name" . '&feed=$matches[1]', 'top' );
557                                         add_rewrite_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$this->name" . '&feed=$matches[1]', 'top' );
558                                 }
559                                 if ( $this->rewrite['pages'] ) {
560                                         add_rewrite_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$this->name" . '&paged=$matches[1]', 'top' );
561                                 }
562                         }
563
564                         $permastruct_args         = $this->rewrite;
565                         $permastruct_args['feed'] = $permastruct_args['feeds'];
566                         add_permastruct( $this->name, "{$this->rewrite['slug']}/%$this->name%", $permastruct_args );
567                 }
568         }
569
570         /**
571          * Registers the post type meta box if a custom callback was specified.
572          *
573          * @since 4.6.0
574          * @access public
575          */
576         public function register_meta_boxes() {
577                 if ( $this->register_meta_box_cb ) {
578                         add_action( 'add_meta_boxes_' . $this->name, $this->register_meta_box_cb, 10, 1 );
579                 }
580         }
581
582         /**
583          * Adds the future post hook action for the post type.
584          *
585          * @since 4.6.0
586          * @access public
587          */
588         public function add_hooks() {
589                 add_action( 'future_' . $this->name, '_future_post_hook', 5, 2 );
590         }
591
592         /**
593          * Registers the taxonomies for the post type.
594          *
595          * @since 4.6.0
596          * @access public
597          */
598         public function register_taxonomies() {
599                 foreach ( $this->taxonomies as $taxonomy ) {
600                         register_taxonomy_for_object_type( $taxonomy, $this->name );
601                 }
602         }
603
604         /**
605          * Removes the features support for the post type.
606          *
607          * @since 4.6.0
608          * @access public
609          *
610          * @global array $_wp_post_type_features Post type features.
611          */
612         public function remove_supports() {
613                 global $_wp_post_type_features;
614
615                 unset( $_wp_post_type_features[ $this->name ] );
616         }
617
618         /**
619          * Removes any rewrite rules, permastructs, and rules for the post type.
620          *
621          * @since 4.6.0
622          * @access public
623          *
624          * @global WP_Rewrite $wp_rewrite          WordPress rewrite component.
625          * @global WP         $wp                  Current WordPress environment instance.
626          * @global array      $post_type_meta_caps Used to remove meta capabilities.
627          */
628         public function remove_rewrite_rules() {
629                 global $wp, $wp_rewrite, $post_type_meta_caps;
630
631                 // Remove query var.
632                 if ( false !== $this->query_var ) {
633                         $wp->remove_query_var( $this->query_var );
634                 }
635
636                 // Remove any rewrite rules, permastructs, and rules.
637                 if ( false !== $this->rewrite ) {
638                         remove_rewrite_tag( "%$this->name%" );
639                         remove_permastruct( $this->name );
640                         foreach ( $wp_rewrite->extra_rules_top as $regex => $query ) {
641                                 if ( false !== strpos( $query, "index.php?post_type=$this->name" ) ) {
642                                         unset( $wp_rewrite->extra_rules_top[ $regex ] );
643                                 }
644                         }
645                 }
646
647                 // Remove registered custom meta capabilities.
648                 foreach ( $this->cap as $cap ) {
649                         unset( $post_type_meta_caps[ $cap ] );
650                 }
651         }
652
653         /**
654          * Unregisters the post type meta box if a custom callback was specified.
655          *
656          * @since 4.6.0
657          * @access public
658          */
659         public function unregister_meta_boxes() {
660                 if ( $this->register_meta_box_cb ) {
661                         remove_action( 'add_meta_boxes_' . $this->name, $this->register_meta_box_cb, 10 );
662                 }
663         }
664
665         /**
666          * Removes the post type from all taxonomies.
667          *
668          * @since 4.6.0
669          * @access public
670          */
671         public function unregister_taxonomies() {
672                 foreach ( get_object_taxonomies( $this->name ) as $taxonomy ) {
673                         unregister_taxonomy_for_object_type( $taxonomy, $this->name );
674                 }
675         }
676
677         /**
678          * Removes the future post hook action for the post type.
679          *
680          * @since 4.6.0
681          * @access public
682          */
683         public function remove_hooks() {
684                 remove_action( 'future_' . $this->name, '_future_post_hook', 5 );
685         }
686 }