You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

687 lines
18 KiB

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