Sin descripción

class-wp-taxonomy.php 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <?php
  2. /**
  3. * Taxonomy API: WP_Taxonomy class
  4. *
  5. * @package WordPress
  6. * @subpackage Taxonomy
  7. * @since 4.7.0
  8. */
  9. /**
  10. * Core class used for interacting with taxonomies.
  11. *
  12. * @since 4.7.0
  13. */
  14. final class WP_Taxonomy {
  15. /**
  16. * Taxonomy key.
  17. *
  18. * @since 4.7.0
  19. * @var string
  20. */
  21. public $name;
  22. /**
  23. * Name of the taxonomy shown in the menu. Usually plural.
  24. *
  25. * @since 4.7.0
  26. * @var string
  27. */
  28. public $label;
  29. /**
  30. * Labels object for this taxonomy.
  31. *
  32. * If not set, tag labels are inherited for non-hierarchical types
  33. * and category labels for hierarchical ones.
  34. *
  35. * @see get_taxonomy_labels()
  36. *
  37. * @since 4.7.0
  38. * @var stdClass
  39. */
  40. public $labels;
  41. /**
  42. * Default labels.
  43. *
  44. * @since 6.0.0
  45. * @var (string|null)[][] $default_labels
  46. */
  47. protected static $default_labels = array();
  48. /**
  49. * A short descriptive summary of what the taxonomy is for.
  50. *
  51. * @since 4.7.0
  52. * @var string
  53. */
  54. public $description = '';
  55. /**
  56. * Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users.
  57. *
  58. * @since 4.7.0
  59. * @var bool
  60. */
  61. public $public = true;
  62. /**
  63. * Whether the taxonomy is publicly queryable.
  64. *
  65. * @since 4.7.0
  66. * @var bool
  67. */
  68. public $publicly_queryable = true;
  69. /**
  70. * Whether the taxonomy is hierarchical.
  71. *
  72. * @since 4.7.0
  73. * @var bool
  74. */
  75. public $hierarchical = false;
  76. /**
  77. * Whether to generate and allow a UI for managing terms in this taxonomy in the admin.
  78. *
  79. * @since 4.7.0
  80. * @var bool
  81. */
  82. public $show_ui = true;
  83. /**
  84. * Whether to show the taxonomy in the admin menu.
  85. *
  86. * If true, the taxonomy is shown as a submenu of the object type menu. If false, no menu is shown.
  87. *
  88. * @since 4.7.0
  89. * @var bool
  90. */
  91. public $show_in_menu = true;
  92. /**
  93. * Whether the taxonomy is available for selection in navigation menus.
  94. *
  95. * @since 4.7.0
  96. * @var bool
  97. */
  98. public $show_in_nav_menus = true;
  99. /**
  100. * Whether to list the taxonomy in the tag cloud widget controls.
  101. *
  102. * @since 4.7.0
  103. * @var bool
  104. */
  105. public $show_tagcloud = true;
  106. /**
  107. * Whether to show the taxonomy in the quick/bulk edit panel.
  108. *
  109. * @since 4.7.0
  110. * @var bool
  111. */
  112. public $show_in_quick_edit = true;
  113. /**
  114. * Whether to display a column for the taxonomy on its post type listing screens.
  115. *
  116. * @since 4.7.0
  117. * @var bool
  118. */
  119. public $show_admin_column = false;
  120. /**
  121. * The callback function for the meta box display.
  122. *
  123. * @since 4.7.0
  124. * @var bool|callable
  125. */
  126. public $meta_box_cb = null;
  127. /**
  128. * The callback function for sanitizing taxonomy data saved from a meta box.
  129. *
  130. * @since 5.1.0
  131. * @var callable
  132. */
  133. public $meta_box_sanitize_cb = null;
  134. /**
  135. * An array of object types this taxonomy is registered for.
  136. *
  137. * @since 4.7.0
  138. * @var string[]
  139. */
  140. public $object_type = null;
  141. /**
  142. * Capabilities for this taxonomy.
  143. *
  144. * @since 4.7.0
  145. * @var stdClass
  146. */
  147. public $cap;
  148. /**
  149. * Rewrites information for this taxonomy.
  150. *
  151. * @since 4.7.0
  152. * @var array|false
  153. */
  154. public $rewrite;
  155. /**
  156. * Query var string for this taxonomy.
  157. *
  158. * @since 4.7.0
  159. * @var string|false
  160. */
  161. public $query_var;
  162. /**
  163. * Function that will be called when the count is updated.
  164. *
  165. * @since 4.7.0
  166. * @var callable
  167. */
  168. public $update_count_callback;
  169. /**
  170. * Whether this taxonomy should appear in the REST API.
  171. *
  172. * Default false. If true, standard endpoints will be registered with
  173. * respect to $rest_base and $rest_controller_class.
  174. *
  175. * @since 4.7.4
  176. * @var bool $show_in_rest
  177. */
  178. public $show_in_rest;
  179. /**
  180. * The base path for this taxonomy's REST API endpoints.
  181. *
  182. * @since 4.7.4
  183. * @var string|bool $rest_base
  184. */
  185. public $rest_base;
  186. /**
  187. * The namespace for this taxonomy's REST API endpoints.
  188. *
  189. * @since 5.9.0
  190. * @var string|bool $rest_namespace
  191. */
  192. public $rest_namespace;
  193. /**
  194. * The controller for this taxonomy's REST API endpoints.
  195. *
  196. * Custom controllers must extend WP_REST_Controller.
  197. *
  198. * @since 4.7.4
  199. * @var string|bool $rest_controller_class
  200. */
  201. public $rest_controller_class;
  202. /**
  203. * The controller instance for this taxonomy's REST API endpoints.
  204. *
  205. * Lazily computed. Should be accessed using {@see WP_Taxonomy::get_rest_controller()}.
  206. *
  207. * @since 5.5.0
  208. * @var WP_REST_Controller $rest_controller
  209. */
  210. public $rest_controller;
  211. /**
  212. * The default term name for this taxonomy. If you pass an array you have
  213. * to set 'name' and optionally 'slug' and 'description'.
  214. *
  215. * @since 5.5.0
  216. * @var array|string
  217. */
  218. public $default_term;
  219. /**
  220. * Whether terms in this taxonomy should be sorted in the order they are provided to `wp_set_object_terms()`.
  221. *
  222. * Use this in combination with `'orderby' => 'term_order'` when fetching terms.
  223. *
  224. * @since 2.5.0
  225. * @var bool|null
  226. */
  227. public $sort = null;
  228. /**
  229. * Array of arguments to automatically use inside `wp_get_object_terms()` for this taxonomy.
  230. *
  231. * @since 2.6.0
  232. * @var array|null
  233. */
  234. public $args = null;
  235. /**
  236. * Whether it is a built-in taxonomy.
  237. *
  238. * @since 4.7.0
  239. * @var bool
  240. */
  241. public $_builtin;
  242. /**
  243. * Constructor.
  244. *
  245. * See the register_taxonomy() function for accepted arguments for `$args`.
  246. *
  247. * @since 4.7.0
  248. *
  249. * @global WP $wp Current WordPress environment instance.
  250. *
  251. * @param string $taxonomy Taxonomy key, must not exceed 32 characters.
  252. * @param array|string $object_type Name of the object type for the taxonomy object.
  253. * @param array|string $args Optional. Array or query string of arguments for registering a taxonomy.
  254. * Default empty array.
  255. */
  256. public function __construct( $taxonomy, $object_type, $args = array() ) {
  257. $this->name = $taxonomy;
  258. $this->set_props( $object_type, $args );
  259. }
  260. /**
  261. * Sets taxonomy properties.
  262. *
  263. * See the register_taxonomy() function for accepted arguments for `$args`.
  264. *
  265. * @since 4.7.0
  266. *
  267. * @param string|string[] $object_type Name or array of names of the object types for the taxonomy.
  268. * @param array|string $args Array or query string of arguments for registering a taxonomy.
  269. */
  270. public function set_props( $object_type, $args ) {
  271. $args = wp_parse_args( $args );
  272. /**
  273. * Filters the arguments for registering a taxonomy.
  274. *
  275. * @since 4.4.0
  276. *
  277. * @param array $args Array of arguments for registering a taxonomy.
  278. * See the register_taxonomy() function for accepted arguments.
  279. * @param string $taxonomy Taxonomy key.
  280. * @param string[] $object_type Array of names of object types for the taxonomy.
  281. */
  282. $args = apply_filters( 'register_taxonomy_args', $args, $this->name, (array) $object_type );
  283. $taxonomy = $this->name;
  284. /**
  285. * Filters the arguments for registering a specific taxonomy.
  286. *
  287. * The dynamic portion of the filter name, `$taxonomy`, refers to the taxonomy key.
  288. *
  289. * Possible hook names include:
  290. *
  291. * - `register_category_taxonomy_args`
  292. * - `register_post_tag_taxonomy_args`
  293. *
  294. * @since 6.0.0
  295. *
  296. * @param array $args Array of arguments for registering a taxonomy.
  297. * See the register_taxonomy() function for accepted arguments.
  298. * @param string $taxonomy Taxonomy key.
  299. * @param string[] $object_type Array of names of object types for the taxonomy.
  300. */
  301. $args = apply_filters( "register_{$taxonomy}_taxonomy_args", $args, $this->name, (array) $object_type );
  302. $defaults = array(
  303. 'labels' => array(),
  304. 'description' => '',
  305. 'public' => true,
  306. 'publicly_queryable' => null,
  307. 'hierarchical' => false,
  308. 'show_ui' => null,
  309. 'show_in_menu' => null,
  310. 'show_in_nav_menus' => null,
  311. 'show_tagcloud' => null,
  312. 'show_in_quick_edit' => null,
  313. 'show_admin_column' => false,
  314. 'meta_box_cb' => null,
  315. 'meta_box_sanitize_cb' => null,
  316. 'capabilities' => array(),
  317. 'rewrite' => true,
  318. 'query_var' => $this->name,
  319. 'update_count_callback' => '',
  320. 'show_in_rest' => false,
  321. 'rest_base' => false,
  322. 'rest_namespace' => false,
  323. 'rest_controller_class' => false,
  324. 'default_term' => null,
  325. 'sort' => null,
  326. 'args' => null,
  327. '_builtin' => false,
  328. );
  329. $args = array_merge( $defaults, $args );
  330. // If not set, default to the setting for 'public'.
  331. if ( null === $args['publicly_queryable'] ) {
  332. $args['publicly_queryable'] = $args['public'];
  333. }
  334. if ( false !== $args['query_var'] && ( is_admin() || false !== $args['publicly_queryable'] ) ) {
  335. if ( true === $args['query_var'] ) {
  336. $args['query_var'] = $this->name;
  337. } else {
  338. $args['query_var'] = sanitize_title_with_dashes( $args['query_var'] );
  339. }
  340. } else {
  341. // Force 'query_var' to false for non-public taxonomies.
  342. $args['query_var'] = false;
  343. }
  344. if ( false !== $args['rewrite'] && ( is_admin() || get_option( 'permalink_structure' ) ) ) {
  345. $args['rewrite'] = wp_parse_args(
  346. $args['rewrite'],
  347. array(
  348. 'with_front' => true,
  349. 'hierarchical' => false,
  350. 'ep_mask' => EP_NONE,
  351. )
  352. );
  353. if ( empty( $args['rewrite']['slug'] ) ) {
  354. $args['rewrite']['slug'] = sanitize_title_with_dashes( $this->name );
  355. }
  356. }
  357. // If not set, default to the setting for 'public'.
  358. if ( null === $args['show_ui'] ) {
  359. $args['show_ui'] = $args['public'];
  360. }
  361. // If not set, default to the setting for 'show_ui'.
  362. if ( null === $args['show_in_menu'] || ! $args['show_ui'] ) {
  363. $args['show_in_menu'] = $args['show_ui'];
  364. }
  365. // If not set, default to the setting for 'public'.
  366. if ( null === $args['show_in_nav_menus'] ) {
  367. $args['show_in_nav_menus'] = $args['public'];
  368. }
  369. // If not set, default to the setting for 'show_ui'.
  370. if ( null === $args['show_tagcloud'] ) {
  371. $args['show_tagcloud'] = $args['show_ui'];
  372. }
  373. // If not set, default to the setting for 'show_ui'.
  374. if ( null === $args['show_in_quick_edit'] ) {
  375. $args['show_in_quick_edit'] = $args['show_ui'];
  376. }
  377. // If not set, default rest_namespace to wp/v2 if show_in_rest is true.
  378. if ( false === $args['rest_namespace'] && ! empty( $args['show_in_rest'] ) ) {
  379. $args['rest_namespace'] = 'wp/v2';
  380. }
  381. $default_caps = array(
  382. 'manage_terms' => 'manage_categories',
  383. 'edit_terms' => 'manage_categories',
  384. 'delete_terms' => 'manage_categories',
  385. 'assign_terms' => 'edit_posts',
  386. );
  387. $args['cap'] = (object) array_merge( $default_caps, $args['capabilities'] );
  388. unset( $args['capabilities'] );
  389. $args['object_type'] = array_unique( (array) $object_type );
  390. // If not set, use the default meta box.
  391. if ( null === $args['meta_box_cb'] ) {
  392. if ( $args['hierarchical'] ) {
  393. $args['meta_box_cb'] = 'post_categories_meta_box';
  394. } else {
  395. $args['meta_box_cb'] = 'post_tags_meta_box';
  396. }
  397. }
  398. $args['name'] = $this->name;
  399. // Default meta box sanitization callback depends on the value of 'meta_box_cb'.
  400. if ( null === $args['meta_box_sanitize_cb'] ) {
  401. switch ( $args['meta_box_cb'] ) {
  402. case 'post_categories_meta_box':
  403. $args['meta_box_sanitize_cb'] = 'taxonomy_meta_box_sanitize_cb_checkboxes';
  404. break;
  405. case 'post_tags_meta_box':
  406. default:
  407. $args['meta_box_sanitize_cb'] = 'taxonomy_meta_box_sanitize_cb_input';
  408. break;
  409. }
  410. }
  411. // Default taxonomy term.
  412. if ( ! empty( $args['default_term'] ) ) {
  413. if ( ! is_array( $args['default_term'] ) ) {
  414. $args['default_term'] = array( 'name' => $args['default_term'] );
  415. }
  416. $args['default_term'] = wp_parse_args(
  417. $args['default_term'],
  418. array(
  419. 'name' => '',
  420. 'slug' => '',
  421. 'description' => '',
  422. )
  423. );
  424. }
  425. foreach ( $args as $property_name => $property_value ) {
  426. $this->$property_name = $property_value;
  427. }
  428. $this->labels = get_taxonomy_labels( $this );
  429. $this->label = $this->labels->name;
  430. }
  431. /**
  432. * Adds the necessary rewrite rules for the taxonomy.
  433. *
  434. * @since 4.7.0
  435. *
  436. * @global WP $wp Current WordPress environment instance.
  437. */
  438. public function add_rewrite_rules() {
  439. /* @var WP $wp */
  440. global $wp;
  441. // Non-publicly queryable taxonomies should not register query vars, except in the admin.
  442. if ( false !== $this->query_var && $wp ) {
  443. $wp->add_query_var( $this->query_var );
  444. }
  445. if ( false !== $this->rewrite && ( is_admin() || get_option( 'permalink_structure' ) ) ) {
  446. if ( $this->hierarchical && $this->rewrite['hierarchical'] ) {
  447. $tag = '(.+?)';
  448. } else {
  449. $tag = '([^/]+)';
  450. }
  451. add_rewrite_tag( "%$this->name%", $tag, $this->query_var ? "{$this->query_var}=" : "taxonomy=$this->name&term=" );
  452. add_permastruct( $this->name, "{$this->rewrite['slug']}/%$this->name%", $this->rewrite );
  453. }
  454. }
  455. /**
  456. * Removes any rewrite rules, permastructs, and rules for the taxonomy.
  457. *
  458. * @since 4.7.0
  459. *
  460. * @global WP $wp Current WordPress environment instance.
  461. */
  462. public function remove_rewrite_rules() {
  463. /* @var WP $wp */
  464. global $wp;
  465. // Remove query var.
  466. if ( false !== $this->query_var ) {
  467. $wp->remove_query_var( $this->query_var );
  468. }
  469. // Remove rewrite tags and permastructs.
  470. if ( false !== $this->rewrite ) {
  471. remove_rewrite_tag( "%$this->name%" );
  472. remove_permastruct( $this->name );
  473. }
  474. }
  475. /**
  476. * Registers the ajax callback for the meta box.
  477. *
  478. * @since 4.7.0
  479. */
  480. public function add_hooks() {
  481. add_filter( 'wp_ajax_add-' . $this->name, '_wp_ajax_add_hierarchical_term' );
  482. }
  483. /**
  484. * Removes the ajax callback for the meta box.
  485. *
  486. * @since 4.7.0
  487. */
  488. public function remove_hooks() {
  489. remove_filter( 'wp_ajax_add-' . $this->name, '_wp_ajax_add_hierarchical_term' );
  490. }
  491. /**
  492. * Gets the REST API controller for this taxonomy.
  493. *
  494. * Will only instantiate the controller class once per request.
  495. *
  496. * @since 5.5.0
  497. *
  498. * @return WP_REST_Controller|null The controller instance, or null if the taxonomy
  499. * is set not to show in rest.
  500. */
  501. public function get_rest_controller() {
  502. if ( ! $this->show_in_rest ) {
  503. return null;
  504. }
  505. $class = $this->rest_controller_class ? $this->rest_controller_class : WP_REST_Terms_Controller::class;
  506. if ( ! class_exists( $class ) ) {
  507. return null;
  508. }
  509. if ( ! is_subclass_of( $class, WP_REST_Controller::class ) ) {
  510. return null;
  511. }
  512. if ( ! $this->rest_controller ) {
  513. $this->rest_controller = new $class( $this->name );
  514. }
  515. if ( ! ( $this->rest_controller instanceof $class ) ) {
  516. return null;
  517. }
  518. return $this->rest_controller;
  519. }
  520. /**
  521. * Returns the default labels for taxonomies.
  522. *
  523. * @since 6.0.0
  524. *
  525. * @return (string|null)[][] The default labels for taxonomies.
  526. */
  527. public static function get_default_labels() {
  528. if ( ! empty( self::$default_labels ) ) {
  529. return self::$default_labels;
  530. }
  531. $name_field_description = __( 'The name is how it appears on your site.' );
  532. $slug_field_description = __( 'The &#8220;slug&#8221; is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.' );
  533. $parent_field_description = __( 'Assign a parent term to create a hierarchy. The term Jazz, for example, would be the parent of Bebop and Big Band.' );
  534. $desc_field_description = __( 'The description is not prominent by default; however, some themes may show it.' );
  535. self::$default_labels = array(
  536. 'name' => array( _x( 'Tags', 'taxonomy general name' ), _x( 'Categories', 'taxonomy general name' ) ),
  537. 'singular_name' => array( _x( 'Tag', 'taxonomy singular name' ), _x( 'Category', 'taxonomy singular name' ) ),
  538. 'search_items' => array( __( 'Search Tags' ), __( 'Search Categories' ) ),
  539. 'popular_items' => array( __( 'Popular Tags' ), null ),
  540. 'all_items' => array( __( 'All Tags' ), __( 'All Categories' ) ),
  541. 'parent_item' => array( null, __( 'Parent Category' ) ),
  542. 'parent_item_colon' => array( null, __( 'Parent Category:' ) ),
  543. 'name_field_description' => array( $name_field_description, $name_field_description ),
  544. 'slug_field_description' => array( $slug_field_description, $slug_field_description ),
  545. 'parent_field_description' => array( null, $parent_field_description ),
  546. 'desc_field_description' => array( $desc_field_description, $desc_field_description ),
  547. 'edit_item' => array( __( 'Edit Tag' ), __( 'Edit Category' ) ),
  548. 'view_item' => array( __( 'View Tag' ), __( 'View Category' ) ),
  549. 'update_item' => array( __( 'Update Tag' ), __( 'Update Category' ) ),
  550. 'add_new_item' => array( __( 'Add New Tag' ), __( 'Add New Category' ) ),
  551. 'new_item_name' => array( __( 'New Tag Name' ), __( 'New Category Name' ) ),
  552. 'separate_items_with_commas' => array( __( 'Separate tags with commas' ), null ),
  553. 'add_or_remove_items' => array( __( 'Add or remove tags' ), null ),
  554. 'choose_from_most_used' => array( __( 'Choose from the most used tags' ), null ),
  555. 'not_found' => array( __( 'No tags found.' ), __( 'No categories found.' ) ),
  556. 'no_terms' => array( __( 'No tags' ), __( 'No categories' ) ),
  557. 'filter_by_item' => array( null, __( 'Filter by category' ) ),
  558. 'items_list_navigation' => array( __( 'Tags list navigation' ), __( 'Categories list navigation' ) ),
  559. 'items_list' => array( __( 'Tags list' ), __( 'Categories list' ) ),
  560. /* translators: Tab heading when selecting from the most used terms. */
  561. 'most_used' => array( _x( 'Most Used', 'tags' ), _x( 'Most Used', 'categories' ) ),
  562. 'back_to_items' => array( __( '&larr; Go to Tags' ), __( '&larr; Go to Categories' ) ),
  563. 'item_link' => array(
  564. _x( 'Tag Link', 'navigation link block title' ),
  565. _x( 'Category Link', 'navigation link block title' ),
  566. ),
  567. 'item_link_description' => array(
  568. _x( 'A link to a tag.', 'navigation link block description' ),
  569. _x( 'A link to a category.', 'navigation link block description' ),
  570. ),
  571. );
  572. return self::$default_labels;
  573. }
  574. /**
  575. * Resets the cache for the default labels.
  576. *
  577. * @since 6.0.0
  578. */
  579. public static function reset_default_labels() {
  580. self::$default_labels = array();
  581. }
  582. }