/wp-includes/taxonomy.php

  1. <?php 
  2. /** 
  3. * Core Taxonomy API 
  4. * 
  5. * @package WordPress 
  6. * @subpackage Taxonomy 
  7. */ 
  8.  
  9. // 
  10. // Taxonomy Registration 
  11. // 
  12.   
  13. /** 
  14. * Creates the initial taxonomies. 
  15. * 
  16. * This function fires twice: in wp-settings.php before plugins are loaded (for 
  17. * backward compatibility reasons), and again on the {@see 'init'} action. We must 
  18. * avoid registering rewrite rules before the {@see 'init'} action. 
  19. * 
  20. * @since 2.8.0 
  21. * 
  22. * @global WP_Rewrite $wp_rewrite The WordPress rewrite class. 
  23. */ 
  24. function create_initial_taxonomies() { 
  25. global $wp_rewrite; 
  26.  
  27. if ( ! did_action( 'init' ) ) { 
  28. $rewrite = array( 'category' => false, 'post_tag' => false, 'post_format' => false ); 
  29. } else { 
  30.  
  31. /** 
  32. * Filters the post formats rewrite base. 
  33. * 
  34. * @since 3.1.0 
  35. * 
  36. * @param string $context Context of the rewrite base. Default 'type'. 
  37. */ 
  38. $post_format_base = apply_filters( 'post_format_rewrite_base', 'type' ); 
  39. $rewrite = array( 
  40. 'category' => array( 
  41. 'hierarchical' => true,  
  42. 'slug' => get_option('category_base') ? get_option('category_base') : 'category',  
  43. 'with_front' => ! get_option('category_base') || $wp_rewrite->using_index_permalinks(),  
  44. 'ep_mask' => EP_CATEGORIES,  
  45. ),  
  46. 'post_tag' => array( 
  47. 'hierarchical' => false,  
  48. 'slug' => get_option('tag_base') ? get_option('tag_base') : 'tag',  
  49. 'with_front' => ! get_option('tag_base') || $wp_rewrite->using_index_permalinks(),  
  50. 'ep_mask' => EP_TAGS,  
  51. ),  
  52. 'post_format' => $post_format_base ? array( 'slug' => $post_format_base ) : false,  
  53. ); 
  54.  
  55. register_taxonomy( 'category', 'post', array( 
  56. 'hierarchical' => true,  
  57. 'query_var' => 'category_name',  
  58. 'rewrite' => $rewrite['category'],  
  59. 'public' => true,  
  60. 'show_ui' => true,  
  61. 'show_admin_column' => true,  
  62. '_builtin' => true,  
  63. 'capabilities' => array( 
  64. 'manage_terms' => 'manage_categories',  
  65. 'edit_terms' => 'edit_categories',  
  66. 'delete_terms' => 'delete_categories',  
  67. 'assign_terms' => 'assign_categories',  
  68. ),  
  69. 'show_in_rest' => true,  
  70. 'rest_base' => 'categories',  
  71. 'rest_controller_class' => 'WP_REST_Terms_Controller',  
  72. ) ); 
  73.  
  74. register_taxonomy( 'post_tag', 'post', array( 
  75. 'hierarchical' => false,  
  76. 'query_var' => 'tag',  
  77. 'rewrite' => $rewrite['post_tag'],  
  78. 'public' => true,  
  79. 'show_ui' => true,  
  80. 'show_admin_column' => true,  
  81. '_builtin' => true,  
  82. 'capabilities' => array( 
  83. 'manage_terms' => 'manage_post_tags',  
  84. 'edit_terms' => 'edit_post_tags',  
  85. 'delete_terms' => 'delete_post_tags',  
  86. 'assign_terms' => 'assign_post_tags',  
  87. ),  
  88. 'show_in_rest' => true,  
  89. 'rest_base' => 'tags',  
  90. 'rest_controller_class' => 'WP_REST_Terms_Controller',  
  91. ) ); 
  92.  
  93. register_taxonomy( 'nav_menu', 'nav_menu_item', array( 
  94. 'public' => false,  
  95. 'hierarchical' => false,  
  96. 'labels' => array( 
  97. 'name' => __( 'Navigation Menus' ),  
  98. 'singular_name' => __( 'Navigation Menu' ),  
  99. ),  
  100. 'query_var' => false,  
  101. 'rewrite' => false,  
  102. 'show_ui' => false,  
  103. '_builtin' => true,  
  104. 'show_in_nav_menus' => false,  
  105. ) ); 
  106.  
  107. register_taxonomy( 'link_category', 'link', array( 
  108. 'hierarchical' => false,  
  109. 'labels' => array( 
  110. 'name' => __( 'Link Categories' ),  
  111. 'singular_name' => __( 'Link Category' ),  
  112. 'search_items' => __( 'Search Link Categories' ),  
  113. 'popular_items' => null,  
  114. 'all_items' => __( 'All Link Categories' ),  
  115. 'edit_item' => __( 'Edit Link Category' ),  
  116. 'update_item' => __( 'Update Link Category' ),  
  117. 'add_new_item' => __( 'Add New Link Category' ),  
  118. 'new_item_name' => __( 'New Link Category Name' ),  
  119. 'separate_items_with_commas' => null,  
  120. 'add_or_remove_items' => null,  
  121. 'choose_from_most_used' => null,  
  122. ),  
  123. 'capabilities' => array( 
  124. 'manage_terms' => 'manage_links',  
  125. 'edit_terms' => 'manage_links',  
  126. 'delete_terms' => 'manage_links',  
  127. 'assign_terms' => 'manage_links',  
  128. ),  
  129. 'query_var' => false,  
  130. 'rewrite' => false,  
  131. 'public' => false,  
  132. 'show_ui' => true,  
  133. '_builtin' => true,  
  134. ) ); 
  135.  
  136. register_taxonomy( 'post_format', 'post', array( 
  137. 'public' => true,  
  138. 'hierarchical' => false,  
  139. 'labels' => array( 
  140. 'name' => _x( 'Format', 'post format' ),  
  141. 'singular_name' => _x( 'Format', 'post format' ),  
  142. ),  
  143. 'query_var' => true,  
  144. 'rewrite' => $rewrite['post_format'],  
  145. 'show_ui' => false,  
  146. '_builtin' => true,  
  147. 'show_in_nav_menus' => current_theme_supports( 'post-formats' ),  
  148. ) ); 
  149.  
  150. /** 
  151. * Retrieves a list of registered taxonomy names or objects. 
  152. * 
  153. * @since 3.0.0 
  154. * 
  155. * @global array $wp_taxonomies The registered taxonomies. 
  156. * 
  157. * @param array $args Optional. An array of `key => value` arguments to match against the taxonomy objects. 
  158. * Default empty array. 
  159. * @param string $output Optional. The type of output to return in the array. Accepts either taxonomy 'names' 
  160. * or 'objects'. Default 'names'. 
  161. * @param string $operator Optional. The logical operation to perform. Accepts 'and' or 'or'. 'or' means only 
  162. * one element from the array needs to match; 'and' means all elements must match. 
  163. * Default 'and'. 
  164. * @return array A list of taxonomy names or objects. 
  165. */ 
  166. function get_taxonomies( $args = array(), $output = 'names', $operator = 'and' ) { 
  167. global $wp_taxonomies; 
  168.  
  169. $field = ('names' == $output) ? 'name' : false; 
  170.  
  171. return wp_filter_object_list($wp_taxonomies, $args, $operator, $field); 
  172.  
  173. /** 
  174. * Return the names or objects of the taxonomies which are registered for the requested object or object type, such as 
  175. * a post object or post type name. 
  176. * 
  177. * Example: 
  178. * 
  179. * $taxonomies = get_object_taxonomies( 'post' ); 
  180. * 
  181. * This results in: 
  182. * 
  183. * Array( 'category', 'post_tag' ) 
  184. * 
  185. * @since 2.3.0 
  186. * 
  187. * @global array $wp_taxonomies The registered taxonomies. 
  188. * 
  189. * @param array|string|WP_Post $object Name of the type of taxonomy object, or an object (row from posts) 
  190. * @param string $output Optional. The type of output to return in the array. Accepts either 
  191. * taxonomy 'names' or 'objects'. Default 'names'. 
  192. * @return array The names of all taxonomy of $object_type. 
  193. */ 
  194. function get_object_taxonomies( $object, $output = 'names' ) { 
  195. global $wp_taxonomies; 
  196.  
  197. if ( is_object($object) ) { 
  198. if ( $object->post_type == 'attachment' ) 
  199. return get_attachment_taxonomies( $object, $output ); 
  200. $object = $object->post_type; 
  201.  
  202. $object = (array) $object; 
  203.  
  204. $taxonomies = array(); 
  205. foreach ( (array) $wp_taxonomies as $tax_name => $tax_obj ) { 
  206. if ( array_intersect($object, (array) $tax_obj->object_type) ) { 
  207. if ( 'names' == $output ) 
  208. $taxonomies[] = $tax_name; 
  209. else 
  210. $taxonomies[ $tax_name ] = $tax_obj; 
  211.  
  212. return $taxonomies; 
  213.  
  214. /** 
  215. * Retrieves the taxonomy object of $taxonomy. 
  216. * 
  217. * The get_taxonomy function will first check that the parameter string given 
  218. * is a taxonomy object and if it is, it will return it. 
  219. * 
  220. * @since 2.3.0 
  221. * 
  222. * @global array $wp_taxonomies The registered taxonomies. 
  223. * 
  224. * @param string $taxonomy Name of taxonomy object to return. 
  225. * @return WP_Taxonomy|false The Taxonomy Object or false if $taxonomy doesn't exist. 
  226. */ 
  227. function get_taxonomy( $taxonomy ) { 
  228. global $wp_taxonomies; 
  229.  
  230. if ( ! taxonomy_exists( $taxonomy ) ) 
  231. return false; 
  232.  
  233. return $wp_taxonomies[$taxonomy]; 
  234.  
  235. /** 
  236. * Checks that the taxonomy name exists. 
  237. * 
  238. * Formerly is_taxonomy(), introduced in 2.3.0. 
  239. * 
  240. * @since 3.0.0 
  241. * 
  242. * @global array $wp_taxonomies The registered taxonomies. 
  243. * 
  244. * @param string $taxonomy Name of taxonomy object. 
  245. * @return bool Whether the taxonomy exists. 
  246. */ 
  247. function taxonomy_exists( $taxonomy ) { 
  248. global $wp_taxonomies; 
  249.  
  250. return isset( $wp_taxonomies[$taxonomy] ); 
  251.  
  252. /** 
  253. * Whether the taxonomy object is hierarchical. 
  254. * 
  255. * Checks to make sure that the taxonomy is an object first. Then Gets the 
  256. * object, and finally returns the hierarchical value in the object. 
  257. * 
  258. * A false return value might also mean that the taxonomy does not exist. 
  259. * 
  260. * @since 2.3.0 
  261. * 
  262. * @param string $taxonomy Name of taxonomy object. 
  263. * @return bool Whether the taxonomy is hierarchical. 
  264. */ 
  265. function is_taxonomy_hierarchical($taxonomy) { 
  266. if ( ! taxonomy_exists($taxonomy) ) 
  267. return false; 
  268.  
  269. $taxonomy = get_taxonomy($taxonomy); 
  270. return $taxonomy->hierarchical; 
  271.  
  272. /** 
  273. * Creates or modifies a taxonomy object. 
  274. * 
  275. * Note: Do not use before the {@see 'init'} hook. 
  276. * 
  277. * A simple function for creating or modifying a taxonomy object based on the 
  278. * parameters given. The function will accept an array (third optional 
  279. * parameter), along with strings for the taxonomy name and another string for 
  280. * the object type. 
  281. * 
  282. * @since 2.3.0 
  283. * @since 4.2.0 Introduced `show_in_quick_edit` argument. 
  284. * @since 4.4.0 The `show_ui` argument is now enforced on the term editing screen. 
  285. * @since 4.4.0 The `public` argument now controls whether the taxonomy can be queried on the front end. 
  286. * @since 4.5.0 Introduced `publicly_queryable` argument. 
  287. * @since 4.7.0 Introduced `show_in_rest`, 'rest_base' and 'rest_controller_class' 
  288. * arguments to register the Taxonomy in REST API. 
  289. * 
  290. * @global array $wp_taxonomies Registered taxonomies. 
  291. * 
  292. * @param string $taxonomy Taxonomy key, must not exceed 32 characters. 
  293. * @param array|string $object_type Object type or array of object types with which the taxonomy should be associated. 
  294. * @param array|string $args { 
  295. * Optional. Array or query string of arguments for registering a taxonomy. 
  296. * 
  297. * @type array $labels An array of labels for this taxonomy. By default, Tag labels are 
  298. * used for non-hierarchical taxonomies, and Category labels are used 
  299. * for hierarchical taxonomies. See accepted values in 
  300. * get_taxonomy_labels(). Default empty array. 
  301. * @type string $description A short descriptive summary of what the taxonomy is for. Default empty. 
  302. * @type bool $public Whether a taxonomy is intended for use publicly either via 
  303. * the admin interface or by front-end users. The default settings 
  304. * of `$publicly_queryable`, `$show_ui`, and `$show_in_nav_menus` 
  305. * are inherited from `$public`. 
  306. * @type bool $publicly_queryable Whether the taxonomy is publicly queryable. 
  307. * If not set, the default is inherited from `$public` 
  308. * @type bool $hierarchical Whether the taxonomy is hierarchical. Default false. 
  309. * @type bool $show_ui Whether to generate and allow a UI for managing terms in this taxonomy in 
  310. * the admin. If not set, the default is inherited from `$public` 
  311. * (default true). 
  312. * @type bool $show_in_menu Whether to show the taxonomy in the admin menu. If true, the taxonomy is 
  313. * shown as a submenu of the object type menu. If false, no menu is shown. 
  314. * `$show_ui` must be true. If not set, default is inherited from `$show_ui` 
  315. * (default true). 
  316. * @type bool $show_in_nav_menus Makes this taxonomy available for selection in navigation menus. If not 
  317. * set, the default is inherited from `$public` (default true). 
  318. * @type bool $show_in_rest Whether to include the taxonomy in the REST API. 
  319. * @type string $rest_base To change the base url of REST API route. Default is $taxonomy. 
  320. * @type string $rest_controller_class REST API Controller class name. Default is 'WP_REST_Terms_Controller'. 
  321. * @type bool $show_tagcloud Whether to list the taxonomy in the Tag Cloud Widget controls. If not set,  
  322. * the default is inherited from `$show_ui` (default true). 
  323. * @type bool $show_in_quick_edit Whether to show the taxonomy in the quick/bulk edit panel. It not set,  
  324. * the default is inherited from `$show_ui` (default true). 
  325. * @type bool $show_admin_column Whether to display a column for the taxonomy on its post type listing 
  326. * screens. Default false. 
  327. * @type bool|callable $meta_box_cb Provide a callback function for the meta box display. If not set,  
  328. * post_categories_meta_box() is used for hierarchical taxonomies, and 
  329. * post_tags_meta_box() is used for non-hierarchical. If false, no meta 
  330. * box is shown. 
  331. * @type array $capabilities { 
  332. * Array of capabilities for this taxonomy. 
  333. * 
  334. * @type string $manage_terms Default 'manage_categories'. 
  335. * @type string $edit_terms Default 'manage_categories'. 
  336. * @type string $delete_terms Default 'manage_categories'. 
  337. * @type string $assign_terms Default 'edit_posts'. 
  338. * } 
  339. * @type bool|array $rewrite { 
  340. * Triggers the handling of rewrites for this taxonomy. Default true, using $taxonomy as slug. To prevent 
  341. * rewrite, set to false. To specify rewrite rules, an array can be passed with any of these keys: 
  342. * 
  343. * @type string $slug Customize the permastruct slug. Default `$taxonomy` key. 
  344. * @type bool $with_front Should the permastruct be prepended with WP_Rewrite::$front. Default true. 
  345. * @type bool $hierarchical Either hierarchical rewrite tag or not. Default false. 
  346. * @type int $ep_mask Assign an endpoint mask. Default `EP_NONE`. 
  347. * } 
  348. * @type string $query_var Sets the query var key for this taxonomy. Default `$taxonomy` key. If 
  349. * false, a taxonomy cannot be loaded at `?{query_var}={term_slug}`. If a 
  350. * string, the query `?{query_var}={term_slug}` will be valid. 
  351. * @type callable $update_count_callback Works much like a hook, in that it will be called when the count is 
  352. * updated. Default _update_post_term_count() for taxonomies attached 
  353. * to post types, which confirms that the objects are published before 
  354. * counting them. Default _update_generic_term_count() for taxonomies 
  355. * attached to other object types, such as users. 
  356. * @type bool $_builtin This taxonomy is a "built-in" taxonomy. INTERNAL USE ONLY! 
  357. * Default false. 
  358. * } 
  359. * @return WP_Error|void WP_Error, if errors. 
  360. */ 
  361. function register_taxonomy( $taxonomy, $object_type, $args = array() ) { 
  362. global $wp_taxonomies; 
  363.  
  364. if ( ! is_array( $wp_taxonomies ) ) 
  365. $wp_taxonomies = array(); 
  366.  
  367. $args = wp_parse_args( $args ); 
  368.  
  369. if ( empty( $taxonomy ) || strlen( $taxonomy ) > 32 ) { 
  370. _doing_it_wrong( __FUNCTION__, __( 'Taxonomy names must be between 1 and 32 characters in length.' ), '4.2.0' ); 
  371. return new WP_Error( 'taxonomy_length_invalid', __( 'Taxonomy names must be between 1 and 32 characters in length.' ) ); 
  372.  
  373. $taxonomy_object = new WP_Taxonomy( $taxonomy, $object_type, $args ); 
  374. $taxonomy_object->add_rewrite_rules(); 
  375.  
  376. $wp_taxonomies[ $taxonomy ] = $taxonomy_object; 
  377.  
  378. $taxonomy_object->add_hooks(); 
  379.  
  380.  
  381. /** 
  382. * Fires after a taxonomy is registered. 
  383. * 
  384. * @since 3.3.0 
  385. * 
  386. * @param string $taxonomy Taxonomy slug. 
  387. * @param array|string $object_type Object type or array of object types. 
  388. * @param array $args Array of taxonomy registration arguments. 
  389. */ 
  390. do_action( 'registered_taxonomy', $taxonomy, $object_type, (array) $taxonomy_object ); 
  391.  
  392. /** 
  393. * Unregisters a taxonomy. 
  394. * 
  395. * Can not be used to unregister built-in taxonomies. 
  396. * 
  397. * @since 4.5.0 
  398. * 
  399. * @global WP $wp Current WordPress environment instance. 
  400. * @global array $wp_taxonomies List of taxonomies. 
  401. * 
  402. * @param string $taxonomy Taxonomy name. 
  403. * @return bool|WP_Error True on success, WP_Error on failure or if the taxonomy doesn't exist. 
  404. */ 
  405. function unregister_taxonomy( $taxonomy ) { 
  406. if ( ! taxonomy_exists( $taxonomy ) ) { 
  407. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  408.  
  409. $taxonomy_object = get_taxonomy( $taxonomy ); 
  410.  
  411. // Do not allow unregistering internal taxonomies. 
  412. if ( $taxonomy_object->_builtin ) { 
  413. return new WP_Error( 'invalid_taxonomy', __( 'Unregistering a built-in taxonomy is not allowed' ) ); 
  414.  
  415. global $wp_taxonomies; 
  416.  
  417. $taxonomy_object->remove_rewrite_rules(); 
  418. $taxonomy_object->remove_hooks(); 
  419.  
  420. // Remove the taxonomy. 
  421. unset( $wp_taxonomies[ $taxonomy ] ); 
  422.  
  423. /** 
  424. * Fires after a taxonomy is unregistered. 
  425. * 
  426. * @since 4.5.0 
  427. * 
  428. * @param string $taxonomy Taxonomy name. 
  429. */ 
  430. do_action( 'unregistered_taxonomy', $taxonomy ); 
  431.  
  432. return true; 
  433.  
  434. /** 
  435. * Builds an object with all taxonomy labels out of a taxonomy object 
  436. * 
  437. * Accepted keys of the label array in the taxonomy object: 
  438. * 
  439. * - name - general name for the taxonomy, usually plural. The same as and overridden by $tax->label. Default is Tags/Categories 
  440. * - singular_name - name for one object of this taxonomy. Default is Tag/Category 
  441. * - search_items - Default is Search Tags/Search Categories 
  442. * - popular_items - This string isn't used on hierarchical taxonomies. Default is Popular Tags 
  443. * - all_items - Default is All Tags/All Categories 
  444. * - parent_item - This string isn't used on non-hierarchical taxonomies. In hierarchical ones the default is Parent Category 
  445. * - parent_item_colon - The same as `parent_item`, but with colon `:` in the end 
  446. * - edit_item - Default is Edit Tag/Edit Category 
  447. * - view_item - Default is View Tag/View Category 
  448. * - update_item - Default is Update Tag/Update Category 
  449. * - add_new_item - Default is Add New Tag/Add New Category 
  450. * - new_item_name - Default is New Tag Name/New Category Name 
  451. * - separate_items_with_commas - This string isn't used on hierarchical taxonomies. Default is "Separate tags with commas", used in the meta box. 
  452. * - add_or_remove_items - This string isn't used on hierarchical taxonomies. Default is "Add or remove tags", used in the meta box when JavaScript is disabled. 
  453. * - choose_from_most_used - This string isn't used on hierarchical taxonomies. Default is "Choose from the most used tags", used in the meta box. 
  454. * - not_found - Default is "No tags found"/"No categories found", used in the meta box and taxonomy list table. 
  455. * - no_terms - Default is "No tags"/"No categories", used in the posts and media list tables. 
  456. * - items_list_navigation - String for the table pagination hidden heading. 
  457. * - items_list - String for the table hidden heading. 
  458. * 
  459. * Above, the first default value is for non-hierarchical taxonomies (like tags) and the second one is for hierarchical taxonomies (like categories). 
  460. * 
  461. * @todo Better documentation for the labels array. 
  462. * 
  463. * @since 3.0.0 
  464. * @since 4.3.0 Added the `no_terms` label. 
  465. * @since 4.4.0 Added the `items_list_navigation` and `items_list` labels. 
  466. * 
  467. * @param WP_Taxonomy $tax Taxonomy object. 
  468. * @return object object with all the labels as member variables. 
  469. */ 
  470. function get_taxonomy_labels( $tax ) { 
  471. $tax->labels = (array) $tax->labels; 
  472.  
  473. if ( isset( $tax->helps ) && empty( $tax->labels['separate_items_with_commas'] ) ) 
  474. $tax->labels['separate_items_with_commas'] = $tax->helps; 
  475.  
  476. if ( isset( $tax->no_tagcloud ) && empty( $tax->labels['not_found'] ) ) 
  477. $tax->labels['not_found'] = $tax->no_tagcloud; 
  478.  
  479. $nohier_vs_hier_defaults = array( 
  480. 'name' => array( _x( 'Tags', 'taxonomy general name' ), _x( 'Categories', 'taxonomy general name' ) ),  
  481. 'singular_name' => array( _x( 'Tag', 'taxonomy singular name' ), _x( 'Category', 'taxonomy singular name' ) ),  
  482. 'search_items' => array( __( 'Search Tags' ), __( 'Search Categories' ) ),  
  483. 'popular_items' => array( __( 'Popular Tags' ), null ),  
  484. 'all_items' => array( __( 'All Tags' ), __( 'All Categories' ) ),  
  485. 'parent_item' => array( null, __( 'Parent Category' ) ),  
  486. 'parent_item_colon' => array( null, __( 'Parent Category:' ) ),  
  487. 'edit_item' => array( __( 'Edit Tag' ), __( 'Edit Category' ) ),  
  488. 'view_item' => array( __( 'View Tag' ), __( 'View Category' ) ),  
  489. 'update_item' => array( __( 'Update Tag' ), __( 'Update Category' ) ),  
  490. 'add_new_item' => array( __( 'Add New Tag' ), __( 'Add New Category' ) ),  
  491. 'new_item_name' => array( __( 'New Tag Name' ), __( 'New Category Name' ) ),  
  492. 'separate_items_with_commas' => array( __( 'Separate tags with commas' ), null ),  
  493. 'add_or_remove_items' => array( __( 'Add or remove tags' ), null ),  
  494. 'choose_from_most_used' => array( __( 'Choose from the most used tags' ), null ),  
  495. 'not_found' => array( __( 'No tags found.' ), __( 'No categories found.' ) ),  
  496. 'no_terms' => array( __( 'No tags' ), __( 'No categories' ) ),  
  497. 'items_list_navigation' => array( __( 'Tags list navigation' ), __( 'Categories list navigation' ) ),  
  498. 'items_list' => array( __( 'Tags list' ), __( 'Categories list' ) ),  
  499. ); 
  500. $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name']; 
  501.  
  502. $labels = _get_custom_object_labels( $tax, $nohier_vs_hier_defaults ); 
  503.  
  504. $taxonomy = $tax->name; 
  505.  
  506. $default_labels = clone $labels; 
  507.  
  508. /** 
  509. * Filters the labels of a specific taxonomy. 
  510. * 
  511. * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. 
  512. * 
  513. * @since 4.4.0 
  514. * 
  515. * @see get_taxonomy_labels() for the full list of taxonomy labels. 
  516. * 
  517. * @param object $labels Object with labels for the taxonomy as member variables. 
  518. */ 
  519. $labels = apply_filters( "taxonomy_labels_{$taxonomy}", $labels ); 
  520.  
  521. // Ensure that the filtered labels contain all required default values. 
  522. $labels = (object) array_merge( (array) $default_labels, (array) $labels ); 
  523.  
  524. return $labels; 
  525.  
  526. /** 
  527. * Add an already registered taxonomy to an object type. 
  528. * 
  529. * @since 3.0.0 
  530. * 
  531. * @global array $wp_taxonomies The registered taxonomies. 
  532. * 
  533. * @param string $taxonomy Name of taxonomy object. 
  534. * @param string $object_type Name of the object type. 
  535. * @return bool True if successful, false if not. 
  536. */ 
  537. function register_taxonomy_for_object_type( $taxonomy, $object_type) { 
  538. global $wp_taxonomies; 
  539.  
  540. if ( !isset($wp_taxonomies[$taxonomy]) ) 
  541. return false; 
  542.  
  543. if ( ! get_post_type_object($object_type) ) 
  544. return false; 
  545.  
  546. if ( ! in_array( $object_type, $wp_taxonomies[$taxonomy]->object_type ) ) 
  547. $wp_taxonomies[$taxonomy]->object_type[] = $object_type; 
  548.  
  549. // Filter out empties. 
  550. $wp_taxonomies[ $taxonomy ]->object_type = array_filter( $wp_taxonomies[ $taxonomy ]->object_type ); 
  551.  
  552. return true; 
  553.  
  554. /** 
  555. * Remove an already registered taxonomy from an object type. 
  556. * 
  557. * @since 3.7.0 
  558. * 
  559. * @global array $wp_taxonomies The registered taxonomies. 
  560. * 
  561. * @param string $taxonomy Name of taxonomy object. 
  562. * @param string $object_type Name of the object type. 
  563. * @return bool True if successful, false if not. 
  564. */ 
  565. function unregister_taxonomy_for_object_type( $taxonomy, $object_type ) { 
  566. global $wp_taxonomies; 
  567.  
  568. if ( ! isset( $wp_taxonomies[ $taxonomy ] ) ) 
  569. return false; 
  570.  
  571. if ( ! get_post_type_object( $object_type ) ) 
  572. return false; 
  573.  
  574. $key = array_search( $object_type, $wp_taxonomies[ $taxonomy ]->object_type, true ); 
  575. if ( false === $key ) 
  576. return false; 
  577.  
  578. unset( $wp_taxonomies[ $taxonomy ]->object_type[ $key ] ); 
  579. return true; 
  580.  
  581. // 
  582.  // Term API 
  583. // 
  584.   
  585. /** 
  586. * Retrieve object_ids of valid taxonomy and term. 
  587. * 
  588. * The strings of $taxonomies must exist before this function will continue. On 
  589. * failure of finding a valid taxonomy, it will return an WP_Error class, kind 
  590. * of like Exceptions in PHP 5, except you can't catch them. Even so, you can 
  591. * still test for the WP_Error class and get the error message. 
  592. * 
  593. * The $terms aren't checked the same as $taxonomies, but still need to exist 
  594. * for $object_ids to be returned. 
  595. * 
  596. * It is possible to change the order that object_ids is returned by either 
  597. * using PHP sort family functions or using the database by using $args with 
  598. * either ASC or DESC array. The value should be in the key named 'order'. 
  599. * 
  600. * @since 2.3.0 
  601. * 
  602. * @global wpdb $wpdb WordPress database abstraction object. 
  603. * 
  604. * @param int|array $term_ids Term id or array of term ids of terms that will be used. 
  605. * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names. 
  606. * @param array|string $args Change the order of the object_ids, either ASC or DESC. 
  607. * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success. 
  608. * the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found. 
  609. */ 
  610. function get_objects_in_term( $term_ids, $taxonomies, $args = array() ) { 
  611. global $wpdb; 
  612.  
  613. if ( ! is_array( $term_ids ) ) { 
  614. $term_ids = array( $term_ids ); 
  615. if ( ! is_array( $taxonomies ) ) { 
  616. $taxonomies = array( $taxonomies ); 
  617. foreach ( (array) $taxonomies as $taxonomy ) { 
  618. if ( ! taxonomy_exists( $taxonomy ) ) { 
  619. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  620.  
  621. $defaults = array( 'order' => 'ASC' ); 
  622. $args = wp_parse_args( $args, $defaults ); 
  623.  
  624. $order = ( 'desc' == strtolower( $args['order'] ) ) ? 'DESC' : 'ASC'; 
  625.  
  626. $term_ids = array_map('intval', $term_ids ); 
  627.  
  628. $taxonomies = "'" . implode( "', '", array_map( 'esc_sql', $taxonomies ) ) . "'"; 
  629. $term_ids = "'" . implode( "', '", $term_ids ) . "'"; 
  630.  
  631. $object_ids = $wpdb->get_col("SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tt.term_id IN ($term_ids) ORDER BY tr.object_id $order"); 
  632.  
  633. if ( ! $object_ids ) { 
  634. return array(); 
  635. return $object_ids; 
  636.  
  637. /** 
  638. * Given a taxonomy query, generates SQL to be appended to a main query. 
  639. * 
  640. * @since 3.1.0 
  641. * 
  642. * @see WP_Tax_Query 
  643. * 
  644. * @param array $tax_query A compact tax query 
  645. * @param string $primary_table 
  646. * @param string $primary_id_column 
  647. * @return array 
  648. */ 
  649. function get_tax_sql( $tax_query, $primary_table, $primary_id_column ) { 
  650. $tax_query_obj = new WP_Tax_Query( $tax_query ); 
  651. return $tax_query_obj->get_sql( $primary_table, $primary_id_column ); 
  652.  
  653. /** 
  654. * Get all Term data from database by Term ID. 
  655. * 
  656. * The usage of the get_term function is to apply filters to a term object. It 
  657. * is possible to get a term object from the database before applying the 
  658. * filters. 
  659. * 
  660. * $term ID must be part of $taxonomy, to get from the database. Failure, might 
  661. * be able to be captured by the hooks. Failure would be the same value as $wpdb 
  662. * returns for the get_row method. 
  663. * 
  664. * There are two hooks, one is specifically for each term, named 'get_term', and 
  665. * the second is for the taxonomy name, 'term_$taxonomy'. Both hooks gets the 
  666. * term object, and the taxonomy name as parameters. Both hooks are expected to 
  667. * return a Term object. 
  668. * 
  669. * {@see 'get_term'} hook - Takes two parameters the term Object and the taxonomy name. 
  670. * Must return term object. Used in get_term() as a catch-all filter for every 
  671. * $term. 
  672. * 
  673. * {@see 'get_$taxonomy'} hook - Takes two parameters the term Object and the taxonomy 
  674. * name. Must return term object. $taxonomy will be the taxonomy name, so for 
  675. * example, if 'category', it would be 'get_category' as the filter name. Useful 
  676. * for custom taxonomies or plugging into default taxonomies. 
  677. * 
  678. * @todo Better formatting for DocBlock 
  679. * 
  680. * @since 2.3.0 
  681. * @since 4.4.0 Converted to return a WP_Term object if `$output` is `OBJECT`. 
  682. * The `$taxonomy` parameter was made optional. 
  683. * 
  684. * @global wpdb $wpdb WordPress database abstraction object. 
  685. * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param. 
  686. * 
  687. * @param int|WP_Term|object $term If integer, term data will be fetched from the database, or from the cache if 
  688. * available. If stdClass object (as in the results of a database query), will apply 
  689. * filters and return a `WP_Term` object corresponding to the `$term` data. If `WP_Term`,  
  690. * will return `$term`. 
  691. * @param string $taxonomy Optional. Taxonomy name that $term is part of. 
  692. * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to 
  693. * a WP_Term object, an associative array, or a numeric array, respectively. Default OBJECT. 
  694. * @param string $filter Optional, default is raw or no WordPress defined filter will applied. 
  695. * @return array|WP_Term|WP_Error|null Object of the type specified by `$output` on success. When `$output` is 'OBJECT',  
  696. * a WP_Term instance is returned. If taxonomy does not exist, a WP_Error is 
  697. * returned. Returns null for miscellaneous failure. 
  698. */ 
  699. function get_term( $term, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) { 
  700. if ( empty( $term ) ) { 
  701. return new WP_Error( 'invalid_term', __( 'Empty Term' ) ); 
  702.  
  703. if ( $taxonomy && ! taxonomy_exists( $taxonomy ) ) { 
  704. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  705.  
  706. if ( $term instanceof WP_Term ) { 
  707. $_term = $term; 
  708. } elseif ( is_object( $term ) ) { 
  709. if ( empty( $term->filter ) || 'raw' === $term->filter ) { 
  710. $_term = sanitize_term( $term, $taxonomy, 'raw' ); 
  711. $_term = new WP_Term( $_term ); 
  712. } else { 
  713. $_term = WP_Term::get_instance( $term->term_id ); 
  714. } else { 
  715. $_term = WP_Term::get_instance( $term, $taxonomy ); 
  716.  
  717. if ( is_wp_error( $_term ) ) { 
  718. return $_term; 
  719. } elseif ( ! $_term ) { 
  720. return null; 
  721.  
  722. /** 
  723. * Filters a term. 
  724. * 
  725. * @since 2.3.0 
  726. * @since 4.4.0 `$_term` can now also be a WP_Term object. 
  727. * 
  728. * @param int|WP_Term $_term Term object or ID. 
  729. * @param string $taxonomy The taxonomy slug. 
  730. */ 
  731. $_term = apply_filters( 'get_term', $_term, $taxonomy ); 
  732.  
  733. /** 
  734. * Filters a taxonomy. 
  735. * 
  736. * The dynamic portion of the filter name, `$taxonomy`, refers 
  737. * to the taxonomy slug. 
  738. * 
  739. * @since 2.3.0 
  740. * @since 4.4.0 `$_term` can now also be a WP_Term object. 
  741. * 
  742. * @param int|WP_Term $_term Term object or ID. 
  743. * @param string $taxonomy The taxonomy slug. 
  744. */ 
  745. $_term = apply_filters( "get_{$taxonomy}", $_term, $taxonomy ); 
  746.  
  747. // Bail if a filter callback has changed the type of the `$_term` object. 
  748. if ( ! ( $_term instanceof WP_Term ) ) { 
  749. return $_term; 
  750.  
  751. // Sanitize term, according to the specified filter. 
  752. $_term->filter( $filter ); 
  753.  
  754. if ( $output == ARRAY_A ) { 
  755. return $_term->to_array(); 
  756. } elseif ( $output == ARRAY_N ) { 
  757. return array_values( $_term->to_array() ); 
  758.  
  759. return $_term; 
  760.  
  761. /** 
  762. * Get all Term data from database by Term field and data. 
  763. * 
  764. * Warning: $value is not escaped for 'name' $field. You must do it yourself, if 
  765. * required. 
  766. * 
  767. * The default $field is 'id', therefore it is possible to also use null for 
  768. * field, but not recommended that you do so. 
  769. * 
  770. * If $value does not exist, the return value will be false. If $taxonomy exists 
  771. * and $field and $value combinations exist, the Term will be returned. 
  772. * 
  773. * This function will always return the first term that matches the `$field`- 
  774. * `$value`-`$taxonomy` combination specified in the parameters. If your query 
  775. * is likely to match more than one term (as is likely to be the case when 
  776. * `$field` is 'name', for example), consider using get_terms() instead; that 
  777. * way, you will get all matching terms, and can provide your own logic for 
  778. * deciding which one was intended. 
  779. * 
  780. * @todo Better formatting for DocBlock. 
  781. * 
  782. * @since 2.3.0 
  783. * @since 4.4.0 `$taxonomy` is optional if `$field` is 'term_taxonomy_id'. Converted to return 
  784. * a WP_Term object if `$output` is `OBJECT`. 
  785. * 
  786. * @global wpdb $wpdb WordPress database abstraction object. 
  787. * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param. 
  788. * 
  789. * @param string $field Either 'slug', 'name', 'id' (term_id), or 'term_taxonomy_id' 
  790. * @param string|int $value Search for this term value 
  791. * @param string $taxonomy Taxonomy name. Optional, if `$field` is 'term_taxonomy_id'. 
  792. * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to 
  793. * a WP_Term object, an associative array, or a numeric array, respectively. Default OBJECT. 
  794. * @param string $filter Optional, default is raw or no WordPress defined filter will applied. 
  795. * @return WP_Term|array|false WP_Term instance (or array) on success. Will return false if `$taxonomy` does not exist 
  796. * or `$term` was not found. 
  797. */ 
  798. function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) { 
  799. global $wpdb; 
  800.  
  801. // 'term_taxonomy_id' lookups don't require taxonomy checks. 
  802. if ( 'term_taxonomy_id' !== $field && ! taxonomy_exists( $taxonomy ) ) { 
  803. return false; 
  804.  
  805. $tax_clause = $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy ); 
  806.  
  807. if ( 'slug' == $field ) { 
  808. $_field = 't.slug'; 
  809. $value = sanitize_title($value); 
  810. if ( empty($value) ) 
  811. return false; 
  812. } elseif ( 'name' == $field ) { 
  813. // Assume already escaped 
  814. $value = wp_unslash($value); 
  815. $_field = 't.name'; 
  816. } elseif ( 'term_taxonomy_id' == $field ) { 
  817. $value = (int) $value; 
  818. $_field = 'tt.term_taxonomy_id'; 
  819.  
  820. // No `taxonomy` clause when searching by 'term_taxonomy_id'. 
  821. $tax_clause = ''; 
  822. } else { 
  823. $term = get_term( (int) $value, $taxonomy, $output, $filter ); 
  824. if ( is_wp_error( $term ) || is_null( $term ) ) { 
  825. $term = false; 
  826. return $term; 
  827.  
  828. $term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE $_field = %s", $value ) . " $tax_clause LIMIT 1" ); 
  829. if ( ! $term ) 
  830. return false; 
  831.  
  832. // In the case of 'term_taxonomy_id', override the provided `$taxonomy` with whatever we find in the db. 
  833. if ( 'term_taxonomy_id' === $field ) { 
  834. $taxonomy = $term->taxonomy; 
  835.  
  836. wp_cache_add( $term->term_id, $term, 'terms' ); 
  837.  
  838. return get_term( $term, $taxonomy, $output, $filter ); 
  839.  
  840. /** 
  841. * Merge all term children into a single array of their IDs. 
  842. * 
  843. * This recursive function will merge all of the children of $term into the same 
  844. * array of term IDs. Only useful for taxonomies which are hierarchical. 
  845. * 
  846. * Will return an empty array if $term does not exist in $taxonomy. 
  847. * 
  848. * @since 2.3.0 
  849. * 
  850. * @param string $term_id ID of Term to get children. 
  851. * @param string $taxonomy Taxonomy Name. 
  852. * @return array|WP_Error List of Term IDs. WP_Error returned if `$taxonomy` does not exist. 
  853. */ 
  854. function get_term_children( $term_id, $taxonomy ) { 
  855. if ( ! taxonomy_exists( $taxonomy ) ) { 
  856. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  857.  
  858. $term_id = intval( $term_id ); 
  859.  
  860. $terms = _get_term_hierarchy($taxonomy); 
  861.  
  862. if ( ! isset($terms[$term_id]) ) 
  863. return array(); 
  864.  
  865. $children = $terms[$term_id]; 
  866.  
  867. foreach ( (array) $terms[$term_id] as $child ) { 
  868. if ( $term_id == $child ) { 
  869. continue; 
  870.  
  871. if ( isset($terms[$child]) ) 
  872. $children = array_merge($children, get_term_children($child, $taxonomy)); 
  873.  
  874. return $children; 
  875.  
  876. /** 
  877. * Get sanitized Term field. 
  878. * 
  879. * The function is for contextual reasons and for simplicity of usage. 
  880. * 
  881. * @since 2.3.0 
  882. * @since 4.4.0 The `$taxonomy` parameter was made optional. `$term` can also now accept a WP_Term object. 
  883. * 
  884. * @see sanitize_term_field() 
  885. * 
  886. * @param string $field Term field to fetch. 
  887. * @param int|WP_Term $term Term ID or object. 
  888. * @param string $taxonomy Optional. Taxonomy Name. Default empty. 
  889. * @param string $context Optional, default is display. Look at sanitize_term_field() for available options. 
  890. * @return string|int|null|WP_Error Will return an empty string if $term is not an object or if $field is not set in $term. 
  891. */ 
  892. function get_term_field( $field, $term, $taxonomy = '', $context = 'display' ) { 
  893. $term = get_term( $term, $taxonomy ); 
  894. if ( is_wp_error($term) ) 
  895. return $term; 
  896.  
  897. if ( !is_object($term) ) 
  898. return ''; 
  899.  
  900. if ( !isset($term->$field) ) 
  901. return ''; 
  902.  
  903. return sanitize_term_field( $field, $term->$field, $term->term_id, $term->taxonomy, $context ); 
  904.  
  905. /** 
  906. * Sanitizes Term for editing. 
  907. * 
  908. * Return value is sanitize_term() and usage is for sanitizing the term for 
  909. * editing. Function is for contextual and simplicity. 
  910. * 
  911. * @since 2.3.0 
  912. * 
  913. * @param int|object $id Term ID or object. 
  914. * @param string $taxonomy Taxonomy name. 
  915. * @return string|int|null|WP_Error Will return empty string if $term is not an object. 
  916. */ 
  917. function get_term_to_edit( $id, $taxonomy ) { 
  918. $term = get_term( $id, $taxonomy ); 
  919.  
  920. if ( is_wp_error($term) ) 
  921. return $term; 
  922.  
  923. if ( !is_object($term) ) 
  924. return ''; 
  925.  
  926. return sanitize_term($term, $taxonomy, 'edit'); 
  927.  
  928. /** 
  929. * Retrieve the terms in a given taxonomy or list of taxonomies. 
  930. * 
  931. * You can fully inject any customizations to the query before it is sent, as 
  932. * well as control the output with a filter. 
  933. * 
  934. * The {@see 'get_terms'} filter will be called when the cache has the term and will 
  935. * pass the found term along with the array of $taxonomies and array of $args. 
  936. * This filter is also called before the array of terms is passed and will pass 
  937. * the array of terms, along with the $taxonomies and $args. 
  938. * 
  939. * The {@see 'list_terms_exclusions'} filter passes the compiled exclusions along with 
  940. * the $args. 
  941. * 
  942. * The {@see 'get_terms_orderby'} filter passes the `ORDER BY` clause for the query 
  943. * along with the $args array. 
  944. * 
  945. * Prior to 4.5.0, the first parameter of `get_terms()` was a taxonomy or list of taxonomies: 
  946. * 
  947. * $terms = get_terms( 'post_tag', array( 
  948. * 'hide_empty' => false,  
  949. * ) ); 
  950. * 
  951. * Since 4.5.0, taxonomies should be passed via the 'taxonomy' argument in the `$args` array: 
  952. * 
  953. * $terms = get_terms( array( 
  954. * 'taxonomy' => 'post_tag',  
  955. * 'hide_empty' => false,  
  956. * ) ); 
  957. * 
  958. * @since 2.3.0 
  959. * @since 4.2.0 Introduced 'name' and 'childless' parameters. 
  960. * @since 4.4.0 Introduced the ability to pass 'term_id' as an alias of 'id' for the `orderby` parameter. 
  961. * Introduced the 'meta_query' and 'update_term_meta_cache' parameters. Converted to return 
  962. * a list of WP_Term objects. 
  963. * @since 4.5.0 Changed the function signature so that the `$args` array can be provided as the first parameter. 
  964. * Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata. 
  965. * 
  966. * @internal The `$deprecated` parameter is parsed for backward compatibility only. 
  967. * 
  968. * @global wpdb $wpdb WordPress database abstraction object. 
  969. * @global array $wp_filter 
  970. * 
  971. * @param array|string $args { 
  972. * Optional. Array or string of arguments to get terms. 
  973. * 
  974. * @type string|array $taxonomy Taxonomy name, or array of taxonomies, to which results should 
  975. * be limited. 
  976. * @type string $orderby Field(s) to order terms by. Accepts term fields ('name', 'slug',  
  977. * 'term_group', 'term_id', 'id', 'description'), 'count' for term 
  978. * taxonomy count, 'include' to match the 'order' of the $include param,  
  979. * 'meta_value', 'meta_value_num', the value of `$meta_key`, the array 
  980. * keys of `$meta_query`, or 'none' to omit the ORDER BY clause. 
  981. * Defaults to 'name'. 
  982. * @type string $order Whether to order terms in ascending or descending order. 
  983. * Accepts 'ASC' (ascending) or 'DESC' (descending). 
  984. * Default 'ASC'. 
  985. * @type bool|int $hide_empty Whether to hide terms not assigned to any posts. Accepts 
  986. * 1|true or 0|false. Default 1|true. 
  987. * @type array|string $include Array or comma/space-separated string of term ids to include. 
  988. * Default empty array. 
  989. * @type array|string $exclude Array or comma/space-separated string of term ids to exclude. 
  990. * If $include is non-empty, $exclude is ignored. 
  991. * Default empty array. 
  992. * @type array|string $exclude_tree Array or comma/space-separated string of term ids to exclude 
  993. * along with all of their descendant terms. If $include is 
  994. * non-empty, $exclude_tree is ignored. Default empty array. 
  995. * @type int|string $number Maximum number of terms to return. Accepts ''|0 (all) or any 
  996. * positive number. Default ''|0 (all). 
  997. * @type int $offset The number by which to offset the terms query. Default empty. 
  998. * @type string $fields Term fields to query for. Accepts 'all' (returns an array of complete 
  999. * term objects), 'ids' (returns an array of ids), 'id=>parent' (returns 
  1000. * an associative array with ids as keys, parent term IDs as values),  
  1001. * 'names' (returns an array of term names), 'count' (returns the number 
  1002. * of matching terms), 'id=>name' (returns an associative array with ids 
  1003. * as keys, term names as values), or 'id=>slug' (returns an associative 
  1004. * array with ids as keys, term slugs as values). Default 'all'. 
  1005. * @type string|array $name Optional. Name or array of names to return term(s) for. Default empty. 
  1006. * @type string|array $slug Optional. Slug or array of slugs to return term(s) for. Default empty. 
  1007. * @type bool $hierarchical Whether to include terms that have non-empty descendants (even 
  1008. * if $hide_empty is set to true). Default true. 
  1009. * @type string $search Search criteria to match terms. Will be SQL-formatted with 
  1010. * wildcards before and after. Default empty. 
  1011. * @type string $name__like Retrieve terms with criteria by which a term is LIKE $name__like. 
  1012. * Default empty. 
  1013. * @type string $description__like Retrieve terms where the description is LIKE $description__like. 
  1014. * Default empty. 
  1015. * @type bool $pad_counts Whether to pad the quantity of a term's children in the quantity 
  1016. * of each term's "count" object variable. Default false. 
  1017. * @type string $get Whether to return terms regardless of ancestry or whether the terms 
  1018. * are empty. Accepts 'all' or empty (disabled). Default empty. 
  1019. * @type int $child_of Term ID to retrieve child terms of. If multiple taxonomies 
  1020. * are passed, $child_of is ignored. Default 0. 
  1021. * @type int|string $parent Parent term ID to retrieve direct-child terms of. Default empty. 
  1022. * @type bool $childless True to limit results to terms that have no children. This parameter 
  1023. * has no effect on non-hierarchical taxonomies. Default false. 
  1024. * @type string $cache_domain Unique cache key to be produced when this query is stored in an 
  1025. * object cache. Default is 'core'. 
  1026. * @type bool $update_term_meta_cache Whether to prime meta caches for matched terms. Default true. 
  1027. * @type array $meta_query Meta query clauses to limit retrieved terms by. 
  1028. * See `WP_Meta_Query`. Default empty. 
  1029. * @type string $meta_key Limit terms to those matching a specific metadata key. Can be used in 
  1030. * conjunction with `$meta_value`. 
  1031. * @type string $meta_value Limit terms to those matching a specific metadata value. Usually used 
  1032. * in conjunction with `$meta_key`. 
  1033. * } 
  1034. * @param array $deprecated Argument array, when using the legacy function parameter format. If present, this 
  1035. * parameter will be interpreted as `$args`, and the first function parameter will 
  1036. * be parsed as a taxonomy or array of taxonomies. 
  1037. * @return array|int|WP_Error List of WP_Term instances and their children. Will return WP_Error, if any of $taxonomies 
  1038. * do not exist. 
  1039. */ 
  1040. function get_terms( $args = array(), $deprecated = '' ) { 
  1041. global $wpdb; 
  1042.  
  1043. $term_query = new WP_Term_Query(); 
  1044.  
  1045. /** 
  1046. * Legacy argument format ($taxonomy, $args) takes precedence. 
  1047. * 
  1048. * We detect legacy argument format by checking if 
  1049. * (a) a second non-empty parameter is passed, or 
  1050. * (b) the first parameter shares no keys with the default array (ie, it's a list of taxonomies) 
  1051. */ 
  1052. $_args = wp_parse_args( $args ); 
  1053. $key_intersect = array_intersect_key( $term_query->query_var_defaults, (array) $_args ); 
  1054. $do_legacy_args = $deprecated || empty( $key_intersect ); 
  1055.  
  1056. if ( $do_legacy_args ) { 
  1057. $taxonomies = (array) $args; 
  1058. $args = wp_parse_args( $deprecated ); 
  1059. $args['taxonomy'] = $taxonomies; 
  1060. } else { 
  1061. $args = wp_parse_args( $args ); 
  1062. if ( isset( $args['taxonomy'] ) && null !== $args['taxonomy'] ) { 
  1063. $args['taxonomy'] = (array) $args['taxonomy']; 
  1064.  
  1065. if ( ! empty( $args['taxonomy'] ) ) { 
  1066. foreach ( $args['taxonomy'] as $taxonomy ) { 
  1067. if ( ! taxonomy_exists( $taxonomy ) ) { 
  1068. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  1069.  
  1070. $terms = $term_query->query( $args ); 
  1071.  
  1072. // Count queries are not filtered, for legacy reasons. 
  1073. if ( ! is_array( $terms ) ) { 
  1074. return $terms; 
  1075.  
  1076. /** 
  1077. * Filters the found terms. 
  1078. * 
  1079. * @since 2.3.0 
  1080. * @since 4.6.0 Added the `$term_query` parameter. 
  1081. * 
  1082. * @param array $terms Array of found terms. 
  1083. * @param array $taxonomies An array of taxonomies. 
  1084. * @param array $args An array of get_terms() arguments. 
  1085. * @param WP_Term_Query $term_query The WP_Term_Query object. 
  1086. */ 
  1087. return apply_filters( 'get_terms', $terms, $term_query->query_vars['taxonomy'], $term_query->query_vars, $term_query ); 
  1088.  
  1089. /** 
  1090. * Adds metadata to a term. 
  1091. * 
  1092. * @since 4.4.0 
  1093. * 
  1094. * @param int $term_id Term ID. 
  1095. * @param string $meta_key Metadata name. 
  1096. * @param mixed $meta_value Metadata value. 
  1097. * @param bool $unique Optional. Whether to bail if an entry with the same key is found for the term. 
  1098. * Default false. 
  1099. * @return int|WP_Error|bool Meta ID on success. WP_Error when term_id is ambiguous between taxonomies. 
  1100. * False on failure. 
  1101. */ 
  1102. function add_term_meta( $term_id, $meta_key, $meta_value, $unique = false ) { 
  1103. // Bail if term meta table is not installed. 
  1104. if ( get_option( 'db_version' ) < 34370 ) { 
  1105. return false; 
  1106.  
  1107. if ( wp_term_is_shared( $term_id ) ) { 
  1108. return new WP_Error( 'ambiguous_term_id', __( 'Term meta cannot be added to terms that are shared between taxonomies.'), $term_id ); 
  1109.  
  1110. $added = add_metadata( 'term', $term_id, $meta_key, $meta_value, $unique ); 
  1111.  
  1112. // Bust term query cache. 
  1113. if ( $added ) { 
  1114. wp_cache_set( 'last_changed', microtime(), 'terms' ); 
  1115.  
  1116. return $added; 
  1117.  
  1118. /** 
  1119. * Removes metadata matching criteria from a term. 
  1120. * 
  1121. * @since 4.4.0 
  1122. * 
  1123. * @param int $term_id Term ID. 
  1124. * @param string $meta_key Metadata name. 
  1125. * @param mixed $meta_value Optional. Metadata value. If provided, rows will only be removed that match the value. 
  1126. * @return bool True on success, false on failure. 
  1127. */ 
  1128. function delete_term_meta( $term_id, $meta_key, $meta_value = '' ) { 
  1129. // Bail if term meta table is not installed. 
  1130. if ( get_option( 'db_version' ) < 34370 ) { 
  1131. return false; 
  1132.  
  1133. $deleted = delete_metadata( 'term', $term_id, $meta_key, $meta_value ); 
  1134.  
  1135. // Bust term query cache. 
  1136. if ( $deleted ) { 
  1137. wp_cache_set( 'last_changed', microtime(), 'terms' ); 
  1138.  
  1139. return $deleted; 
  1140.  
  1141. /** 
  1142. * Retrieves metadata for a term. 
  1143. * 
  1144. * @since 4.4.0 
  1145. * 
  1146. * @param int $term_id Term ID. 
  1147. * @param string $key Optional. The meta key to retrieve. If no key is provided, fetches all metadata for the term. 
  1148. * @param bool $single Whether to return a single value. If false, an array of all values matching the 
  1149. * `$term_id`/`$key` pair will be returned. Default: false. 
  1150. * @return mixed If `$single` is false, an array of metadata values. If `$single` is true, a single metadata value. 
  1151. */ 
  1152. function get_term_meta( $term_id, $key = '', $single = false ) { 
  1153. // Bail if term meta table is not installed. 
  1154. if ( get_option( 'db_version' ) < 34370 ) { 
  1155. return false; 
  1156.  
  1157. return get_metadata( 'term', $term_id, $key, $single ); 
  1158.  
  1159. /** 
  1160. * Updates term metadata. 
  1161. * 
  1162. * Use the `$prev_value` parameter to differentiate between meta fields with the same key and term ID. 
  1163. * 
  1164. * If the meta field for the term does not exist, it will be added. 
  1165. * 
  1166. * @since 4.4.0 
  1167. * 
  1168. * @param int $term_id Term ID. 
  1169. * @param string $meta_key Metadata key. 
  1170. * @param mixed $meta_value Metadata value. 
  1171. * @param mixed $prev_value Optional. Previous value to check before removing. 
  1172. * @return int|WP_Error|bool Meta ID if the key didn't previously exist. True on successful update. 
  1173. * WP_Error when term_id is ambiguous between taxonomies. False on failure. 
  1174. */ 
  1175. function update_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) { 
  1176. // Bail if term meta table is not installed. 
  1177. if ( get_option( 'db_version' ) < 34370 ) { 
  1178. return false; 
  1179.  
  1180. if ( wp_term_is_shared( $term_id ) ) { 
  1181. return new WP_Error( 'ambiguous_term_id', __( 'Term meta cannot be added to terms that are shared between taxonomies.'), $term_id ); 
  1182.  
  1183. $updated = update_metadata( 'term', $term_id, $meta_key, $meta_value, $prev_value ); 
  1184.  
  1185. // Bust term query cache. 
  1186. if ( $updated ) { 
  1187. wp_cache_set( 'last_changed', microtime(), 'terms' ); 
  1188.  
  1189. return $updated; 
  1190.  
  1191. /** 
  1192. * Updates metadata cache for list of term IDs. 
  1193. * 
  1194. * Performs SQL query to retrieve all metadata for the terms matching `$term_ids` and stores them in the cache. 
  1195. * Subsequent calls to `get_term_meta()` will not need to query the database. 
  1196. * 
  1197. * @since 4.4.0 
  1198. * 
  1199. * @param array $term_ids List of term IDs. 
  1200. * @return array|false Returns false if there is nothing to update. Returns an array of metadata on success. 
  1201. */ 
  1202. function update_termmeta_cache( $term_ids ) { 
  1203. // Bail if term meta table is not installed. 
  1204. if ( get_option( 'db_version' ) < 34370 ) { 
  1205. return; 
  1206.  
  1207. return update_meta_cache( 'term', $term_ids ); 
  1208.  
  1209. /** 
  1210. * Check if Term exists. 
  1211. * 
  1212. * Formerly is_term(), introduced in 2.3.0. 
  1213. * 
  1214. * @since 3.0.0 
  1215. * 
  1216. * @global wpdb $wpdb WordPress database abstraction object. 
  1217. * 
  1218. * @param int|string $term The term to check. Accepts term ID, slug, or name. 
  1219. * @param string $taxonomy The taxonomy name to use 
  1220. * @param int $parent Optional. ID of parent term under which to confine the exists search. 
  1221. * @return mixed Returns null if the term does not exist. Returns the term ID 
  1222. * if no taxonomy is specified and the term ID exists. Returns 
  1223. * an array of the term ID and the term taxonomy ID the taxonomy 
  1224. * is specified and the pairing exists. 
  1225. */ 
  1226. function term_exists( $term, $taxonomy = '', $parent = null ) { 
  1227. global $wpdb; 
  1228.  
  1229. $select = "SELECT term_id FROM $wpdb->terms as t WHERE "; 
  1230. $tax_select = "SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE "; 
  1231.  
  1232. if ( is_int($term) ) { 
  1233. if ( 0 == $term ) 
  1234. return 0; 
  1235. $where = 't.term_id = %d'; 
  1236. if ( !empty($taxonomy) ) 
  1237. return $wpdb->get_row( $wpdb->prepare( $tax_select . $where . " AND tt.taxonomy = %s", $term, $taxonomy ), ARRAY_A ); 
  1238. else 
  1239. return $wpdb->get_var( $wpdb->prepare( $select . $where, $term ) ); 
  1240.  
  1241. $term = trim( wp_unslash( $term ) ); 
  1242. $slug = sanitize_title( $term ); 
  1243.  
  1244. $where = 't.slug = %s'; 
  1245. $else_where = 't.name = %s'; 
  1246. $where_fields = array($slug); 
  1247. $else_where_fields = array($term); 
  1248. $orderby = 'ORDER BY t.term_id ASC'; 
  1249. $limit = 'LIMIT 1'; 
  1250. if ( !empty($taxonomy) ) { 
  1251. if ( is_numeric( $parent ) ) { 
  1252. $parent = (int) $parent; 
  1253. $where_fields[] = $parent; 
  1254. $else_where_fields[] = $parent; 
  1255. $where .= ' AND tt.parent = %d'; 
  1256. $else_where .= ' AND tt.parent = %d'; 
  1257.  
  1258. $where_fields[] = $taxonomy; 
  1259. $else_where_fields[] = $taxonomy; 
  1260.  
  1261. if ( $result = $wpdb->get_row( $wpdb->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE $where AND tt.taxonomy = %s $orderby $limit", $where_fields), ARRAY_A) ) 
  1262. return $result; 
  1263.  
  1264. return $wpdb->get_row( $wpdb->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE $else_where AND tt.taxonomy = %s $orderby $limit", $else_where_fields), ARRAY_A); 
  1265.  
  1266. if ( $result = $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $where $orderby $limit", $where_fields) ) ) 
  1267. return $result; 
  1268.  
  1269. return $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $else_where $orderby $limit", $else_where_fields) ); 
  1270.  
  1271. /** 
  1272. * Check if a term is an ancestor of another term. 
  1273. * 
  1274. * You can use either an id or the term object for both parameters. 
  1275. * 
  1276. * @since 3.4.0 
  1277. * 
  1278. * @param int|object $term1 ID or object to check if this is the parent term. 
  1279. * @param int|object $term2 The child term. 
  1280. * @param string $taxonomy Taxonomy name that $term1 and `$term2` belong to. 
  1281. * @return bool Whether `$term2` is a child of `$term1`. 
  1282. */ 
  1283. function term_is_ancestor_of( $term1, $term2, $taxonomy ) { 
  1284. if ( ! isset( $term1->term_id ) ) 
  1285. $term1 = get_term( $term1, $taxonomy ); 
  1286. if ( ! isset( $term2->parent ) ) 
  1287. $term2 = get_term( $term2, $taxonomy ); 
  1288.  
  1289. if ( empty( $term1->term_id ) || empty( $term2->parent ) ) 
  1290. return false; 
  1291. if ( $term2->parent == $term1->term_id ) 
  1292. return true; 
  1293.  
  1294. return term_is_ancestor_of( $term1, get_term( $term2->parent, $taxonomy ), $taxonomy ); 
  1295.  
  1296. /** 
  1297. * Sanitize Term all fields. 
  1298. * 
  1299. * Relies on sanitize_term_field() to sanitize the term. The difference is that 
  1300. * this function will sanitize <strong>all</strong> fields. The context is based 
  1301. * on sanitize_term_field(). 
  1302. * 
  1303. * The $term is expected to be either an array or an object. 
  1304. * 
  1305. * @since 2.3.0 
  1306. * 
  1307. * @param array|object $term The term to check. 
  1308. * @param string $taxonomy The taxonomy name to use. 
  1309. * @param string $context Optional. Context in which to sanitize the term. Accepts 'edit', 'db',  
  1310. * 'display', 'attribute', or 'js'. Default 'display'. 
  1311. * @return array|object Term with all fields sanitized. 
  1312. */ 
  1313. function sanitize_term($term, $taxonomy, $context = 'display') { 
  1314. $fields = array( 'term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group', 'term_taxonomy_id', 'object_id' ); 
  1315.  
  1316. $do_object = is_object( $term ); 
  1317.  
  1318. $term_id = $do_object ? $term->term_id : (isset($term['term_id']) ? $term['term_id'] : 0); 
  1319.  
  1320. foreach ( (array) $fields as $field ) { 
  1321. if ( $do_object ) { 
  1322. if ( isset($term->$field) ) 
  1323. $term->$field = sanitize_term_field($field, $term->$field, $term_id, $taxonomy, $context); 
  1324. } else { 
  1325. if ( isset($term[$field]) ) 
  1326. $term[$field] = sanitize_term_field($field, $term[$field], $term_id, $taxonomy, $context); 
  1327.  
  1328. if ( $do_object ) 
  1329. $term->filter = $context; 
  1330. else 
  1331. $term['filter'] = $context; 
  1332.  
  1333. return $term; 
  1334.  
  1335. /** 
  1336. * Cleanse the field value in the term based on the context. 
  1337. * 
  1338. * Passing a term field value through the function should be assumed to have 
  1339. * cleansed the value for whatever context the term field is going to be used. 
  1340. * 
  1341. * If no context or an unsupported context is given, then default filters will 
  1342. * be applied. 
  1343. * 
  1344. * There are enough filters for each context to support a custom filtering 
  1345. * without creating your own filter function. Simply create a function that 
  1346. * hooks into the filter you need. 
  1347. * 
  1348. * @since 2.3.0 
  1349. * 
  1350. * @param string $field Term field to sanitize. 
  1351. * @param string $value Search for this term value. 
  1352. * @param int $term_id Term ID. 
  1353. * @param string $taxonomy Taxonomy Name. 
  1354. * @param string $context Context in which to sanitize the term field. Accepts 'edit', 'db', 'display',  
  1355. * 'attribute', or 'js'. 
  1356. * @return mixed Sanitized field. 
  1357. */ 
  1358. function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) { 
  1359. $int_fields = array( 'parent', 'term_id', 'count', 'term_group', 'term_taxonomy_id', 'object_id' ); 
  1360. if ( in_array( $field, $int_fields ) ) { 
  1361. $value = (int) $value; 
  1362. if ( $value < 0 ) 
  1363. $value = 0; 
  1364.  
  1365. if ( 'raw' == $context ) 
  1366. return $value; 
  1367.  
  1368. if ( 'edit' == $context ) { 
  1369.  
  1370. /** 
  1371. * Filters a term field to edit before it is sanitized. 
  1372. * 
  1373. * The dynamic portion of the filter name, `$field`, refers to the term field. 
  1374. * 
  1375. * @since 2.3.0 
  1376. * 
  1377. * @param mixed $value Value of the term field. 
  1378. * @param int $term_id Term ID. 
  1379. * @param string $taxonomy Taxonomy slug. 
  1380. */ 
  1381. $value = apply_filters( "edit_term_{$field}", $value, $term_id, $taxonomy ); 
  1382.  
  1383. /** 
  1384. * Filters the taxonomy field to edit before it is sanitized. 
  1385. * 
  1386. * The dynamic portions of the filter name, `$taxonomy` and `$field`, refer 
  1387. * to the taxonomy slug and taxonomy field, respectively. 
  1388. * 
  1389. * @since 2.3.0 
  1390. * 
  1391. * @param mixed $value Value of the taxonomy field to edit. 
  1392. * @param int $term_id Term ID. 
  1393. */ 
  1394. $value = apply_filters( "edit_{$taxonomy}_{$field}", $value, $term_id ); 
  1395.  
  1396. if ( 'description' == $field ) 
  1397. $value = esc_html($value); // textarea_escaped 
  1398. else 
  1399. $value = esc_attr($value); 
  1400. } elseif ( 'db' == $context ) { 
  1401.  
  1402. /** 
  1403. * Filters a term field value before it is sanitized. 
  1404. * 
  1405. * The dynamic portion of the filter name, `$field`, refers to the term field. 
  1406. * 
  1407. * @since 2.3.0 
  1408. * 
  1409. * @param mixed $value Value of the term field. 
  1410. * @param string $taxonomy Taxonomy slug. 
  1411. */ 
  1412. $value = apply_filters( "pre_term_{$field}", $value, $taxonomy ); 
  1413.  
  1414. /** 
  1415. * Filters a taxonomy field before it is sanitized. 
  1416. * 
  1417. * The dynamic portions of the filter name, `$taxonomy` and `$field`, refer 
  1418. * to the taxonomy slug and field name, respectively. 
  1419. * 
  1420. * @since 2.3.0 
  1421. * 
  1422. * @param mixed $value Value of the taxonomy field. 
  1423. */ 
  1424. $value = apply_filters( "pre_{$taxonomy}_{$field}", $value ); 
  1425.  
  1426. // Back compat filters 
  1427. if ( 'slug' == $field ) { 
  1428. /** 
  1429. * Filters the category nicename before it is sanitized. 
  1430. * 
  1431. * Use the {@see 'pre_$taxonomy_$field'} hook instead. 
  1432. * 
  1433. * @since 2.0.3 
  1434. * 
  1435. * @param string $value The category nicename. 
  1436. */ 
  1437. $value = apply_filters( 'pre_category_nicename', $value ); 
  1438.  
  1439. } elseif ( 'rss' == $context ) { 
  1440.  
  1441. /** 
  1442. * Filters the term field for use in RSS. 
  1443. * 
  1444. * The dynamic portion of the filter name, `$field`, refers to the term field. 
  1445. * 
  1446. * @since 2.3.0 
  1447. * 
  1448. * @param mixed $value Value of the term field. 
  1449. * @param string $taxonomy Taxonomy slug. 
  1450. */ 
  1451. $value = apply_filters( "term_{$field}_rss", $value, $taxonomy ); 
  1452.  
  1453. /** 
  1454. * Filters the taxonomy field for use in RSS. 
  1455. * 
  1456. * The dynamic portions of the hook name, `$taxonomy`, and `$field`, refer 
  1457. * to the taxonomy slug and field name, respectively. 
  1458. * 
  1459. * @since 2.3.0 
  1460. * 
  1461. * @param mixed $value Value of the taxonomy field. 
  1462. */ 
  1463. $value = apply_filters( "{$taxonomy}_{$field}_rss", $value ); 
  1464. } else { 
  1465. // Use display filters by default. 
  1466.  
  1467. /** 
  1468. * Filters the term field sanitized for display. 
  1469. * 
  1470. * The dynamic portion of the filter name, `$field`, refers to the term field name. 
  1471. * 
  1472. * @since 2.3.0 
  1473. * 
  1474. * @param mixed $value Value of the term field. 
  1475. * @param int $term_id Term ID. 
  1476. * @param string $taxonomy Taxonomy slug. 
  1477. * @param string $context Context to retrieve the term field value. 
  1478. */ 
  1479. $value = apply_filters( "term_{$field}", $value, $term_id, $taxonomy, $context ); 
  1480.  
  1481. /** 
  1482. * Filters the taxonomy field sanitized for display. 
  1483. * 
  1484. * The dynamic portions of the filter name, `$taxonomy`, and `$field`, refer 
  1485. * to the taxonomy slug and taxonomy field, respectively. 
  1486. * 
  1487. * @since 2.3.0 
  1488. * 
  1489. * @param mixed $value Value of the taxonomy field. 
  1490. * @param int $term_id Term ID. 
  1491. * @param string $context Context to retrieve the taxonomy field value. 
  1492. */ 
  1493. $value = apply_filters( "{$taxonomy}_{$field}", $value, $term_id, $context ); 
  1494.  
  1495. if ( 'attribute' == $context ) { 
  1496. $value = esc_attr($value); 
  1497. } elseif ( 'js' == $context ) { 
  1498. $value = esc_js($value); 
  1499. return $value; 
  1500.  
  1501. /** 
  1502. * Count how many terms are in Taxonomy. 
  1503. * 
  1504. * Default $args is 'hide_empty' which can be 'hide_empty=true' or array('hide_empty' => true). 
  1505. * 
  1506. * @since 2.3.0 
  1507. * 
  1508. * @param string $taxonomy Taxonomy name. 
  1509. * @param array|string $args Optional. Array of arguments that get passed to get_terms(). 
  1510. * Default empty array. 
  1511. * @return array|int|WP_Error Number of terms in that taxonomy or WP_Error if the taxonomy does not exist. 
  1512. */ 
  1513. function wp_count_terms( $taxonomy, $args = array() ) { 
  1514. $defaults = array('hide_empty' => false); 
  1515. $args = wp_parse_args($args, $defaults); 
  1516.  
  1517. // backward compatibility 
  1518. if ( isset($args['ignore_empty']) ) { 
  1519. $args['hide_empty'] = $args['ignore_empty']; 
  1520. unset($args['ignore_empty']); 
  1521.  
  1522. $args['fields'] = 'count'; 
  1523.  
  1524. return get_terms($taxonomy, $args); 
  1525.  
  1526. /** 
  1527. * Will unlink the object from the taxonomy or taxonomies. 
  1528. * 
  1529. * Will remove all relationships between the object and any terms in 
  1530. * a particular taxonomy or taxonomies. Does not remove the term or 
  1531. * taxonomy itself. 
  1532. * 
  1533. * @since 2.3.0 
  1534. * 
  1535. * @param int $object_id The term Object Id that refers to the term. 
  1536. * @param string|array $taxonomies List of Taxonomy Names or single Taxonomy name. 
  1537. */ 
  1538. function wp_delete_object_term_relationships( $object_id, $taxonomies ) { 
  1539. $object_id = (int) $object_id; 
  1540.  
  1541. if ( !is_array($taxonomies) ) 
  1542. $taxonomies = array($taxonomies); 
  1543.  
  1544. foreach ( (array) $taxonomies as $taxonomy ) { 
  1545. $term_ids = wp_get_object_terms( $object_id, $taxonomy, array( 'fields' => 'ids' ) ); 
  1546. $term_ids = array_map( 'intval', $term_ids ); 
  1547. wp_remove_object_terms( $object_id, $term_ids, $taxonomy ); 
  1548.  
  1549. /** 
  1550. * Removes a term from the database. 
  1551. * 
  1552. * If the term is a parent of other terms, then the children will be updated to 
  1553. * that term's parent. 
  1554. * 
  1555. * Metadata associated with the term will be deleted. 
  1556. * 
  1557. * @since 2.3.0 
  1558. * 
  1559. * @global wpdb $wpdb WordPress database abstraction object. 
  1560. * 
  1561. * @param int $term Term ID. 
  1562. * @param string $taxonomy Taxonomy Name. 
  1563. * @param array|string $args { 
  1564. * Optional. Array of arguments to override the default term ID. Default empty array. 
  1565. * 
  1566. * @type int $default The term ID to make the default term. This will only override 
  1567. * the terms found if there is only one term found. Any other and 
  1568. * the found terms are used. 
  1569. * @type bool $force_default Optional. Whether to force the supplied term as default to be 
  1570. * assigned even if the object was not going to be term-less. 
  1571. * Default false. 
  1572. * } 
  1573. * @return bool|int|WP_Error True on success, false if term does not exist. Zero on attempted 
  1574. * deletion of default Category. WP_Error if the taxonomy does not exist. 
  1575. */ 
  1576. function wp_delete_term( $term, $taxonomy, $args = array() ) { 
  1577. global $wpdb; 
  1578.  
  1579. $term = (int) $term; 
  1580.  
  1581. if ( ! $ids = term_exists($term, $taxonomy) ) 
  1582. return false; 
  1583. if ( is_wp_error( $ids ) ) 
  1584. return $ids; 
  1585.  
  1586. $tt_id = $ids['term_taxonomy_id']; 
  1587.  
  1588. $defaults = array(); 
  1589.  
  1590. if ( 'category' == $taxonomy ) { 
  1591. $defaults['default'] = get_option( 'default_category' ); 
  1592. if ( $defaults['default'] == $term ) 
  1593. return 0; // Don't delete the default category 
  1594.  
  1595. $args = wp_parse_args($args, $defaults); 
  1596.  
  1597. if ( isset( $args['default'] ) ) { 
  1598. $default = (int) $args['default']; 
  1599. if ( ! term_exists( $default, $taxonomy ) ) { 
  1600. unset( $default ); 
  1601.  
  1602. if ( isset( $args['force_default'] ) ) { 
  1603. $force_default = $args['force_default']; 
  1604.  
  1605. /** 
  1606. * Fires when deleting a term, before any modifications are made to posts or terms. 
  1607. * 
  1608. * @since 4.1.0 
  1609. * 
  1610. * @param int $term Term ID. 
  1611. * @param string $taxonomy Taxonomy Name. 
  1612. */ 
  1613. do_action( 'pre_delete_term', $term, $taxonomy ); 
  1614.  
  1615. // Update children to point to new parent 
  1616. if ( is_taxonomy_hierarchical($taxonomy) ) { 
  1617. $term_obj = get_term($term, $taxonomy); 
  1618. if ( is_wp_error( $term_obj ) ) 
  1619. return $term_obj; 
  1620. $parent = $term_obj->parent; 
  1621.  
  1622. $edit_ids = $wpdb->get_results( "SELECT term_id, term_taxonomy_id FROM $wpdb->term_taxonomy WHERE `parent` = " . (int)$term_obj->term_id ); 
  1623. $edit_tt_ids = wp_list_pluck( $edit_ids, 'term_taxonomy_id' ); 
  1624.  
  1625. /** 
  1626. * Fires immediately before a term to delete's children are reassigned a parent. 
  1627. * 
  1628. * @since 2.9.0 
  1629. * 
  1630. * @param array $edit_tt_ids An array of term taxonomy IDs for the given term. 
  1631. */ 
  1632. do_action( 'edit_term_taxonomies', $edit_tt_ids ); 
  1633.  
  1634. $wpdb->update( $wpdb->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id) + compact( 'taxonomy' ) ); 
  1635.  
  1636. // Clean the cache for all child terms. 
  1637. $edit_term_ids = wp_list_pluck( $edit_ids, 'term_id' ); 
  1638. clean_term_cache( $edit_term_ids, $taxonomy ); 
  1639.  
  1640. /** 
  1641. * Fires immediately after a term to delete's children are reassigned a parent. 
  1642. * 
  1643. * @since 2.9.0 
  1644. * 
  1645. * @param array $edit_tt_ids An array of term taxonomy IDs for the given term. 
  1646. */ 
  1647. do_action( 'edited_term_taxonomies', $edit_tt_ids ); 
  1648.  
  1649. // Get the term before deleting it or its term relationships so we can pass to actions below. 
  1650. $deleted_term = get_term( $term, $taxonomy ); 
  1651.  
  1652. $object_ids = (array) $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) ); 
  1653.  
  1654. foreach ( $object_ids as $object_id ) { 
  1655. $terms = wp_get_object_terms( $object_id, $taxonomy, array( 'fields' => 'ids', 'orderby' => 'none' ) ); 
  1656. if ( 1 == count($terms) && isset($default) ) { 
  1657. $terms = array($default); 
  1658. } else { 
  1659. $terms = array_diff($terms, array($term)); 
  1660. if (isset($default) && isset($force_default) && $force_default) 
  1661. $terms = array_merge($terms, array($default)); 
  1662. $terms = array_map('intval', $terms); 
  1663. wp_set_object_terms( $object_id, $terms, $taxonomy ); 
  1664.  
  1665. // Clean the relationship caches for all object types using this term. 
  1666. $tax_object = get_taxonomy( $taxonomy ); 
  1667. foreach ( $tax_object->object_type as $object_type ) 
  1668. clean_object_term_cache( $object_ids, $object_type ); 
  1669.  
  1670. $term_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->termmeta WHERE term_id = %d ", $term ) ); 
  1671. foreach ( $term_meta_ids as $mid ) { 
  1672. delete_metadata_by_mid( 'term', $mid ); 
  1673.  
  1674. /** 
  1675. * Fires immediately before a term taxonomy ID is deleted. 
  1676. * 
  1677. * @since 2.9.0 
  1678. * 
  1679. * @param int $tt_id Term taxonomy ID. 
  1680. */ 
  1681. do_action( 'delete_term_taxonomy', $tt_id ); 
  1682. $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) ); 
  1683.  
  1684. /** 
  1685. * Fires immediately after a term taxonomy ID is deleted. 
  1686. * 
  1687. * @since 2.9.0 
  1688. * 
  1689. * @param int $tt_id Term taxonomy ID. 
  1690. */ 
  1691. do_action( 'deleted_term_taxonomy', $tt_id ); 
  1692.  
  1693. // Delete the term if no taxonomies use it. 
  1694. if ( !$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d", $term) ) ) 
  1695. $wpdb->delete( $wpdb->terms, array( 'term_id' => $term ) ); 
  1696.  
  1697. clean_term_cache($term, $taxonomy); 
  1698.  
  1699. /** 
  1700. * Fires after a term is deleted from the database and the cache is cleaned. 
  1701. * 
  1702. * @since 2.5.0 
  1703. * @since 4.5.0 Introduced the `$object_ids` argument. 
  1704. * 
  1705. * @param int $term Term ID. 
  1706. * @param int $tt_id Term taxonomy ID. 
  1707. * @param string $taxonomy Taxonomy slug. 
  1708. * @param mixed $deleted_term Copy of the already-deleted term, in the form specified 
  1709. * by the parent function. WP_Error otherwise. 
  1710. * @param array $object_ids List of term object IDs. 
  1711. */ 
  1712. do_action( 'delete_term', $term, $tt_id, $taxonomy, $deleted_term, $object_ids ); 
  1713.  
  1714. /** 
  1715. * Fires after a term in a specific taxonomy is deleted. 
  1716. * 
  1717. * The dynamic portion of the hook name, `$taxonomy`, refers to the specific 
  1718. * taxonomy the term belonged to. 
  1719. * 
  1720. * @since 2.3.0 
  1721. * @since 4.5.0 Introduced the `$object_ids` argument. 
  1722. * 
  1723. * @param int $term Term ID. 
  1724. * @param int $tt_id Term taxonomy ID. 
  1725. * @param mixed $deleted_term Copy of the already-deleted term, in the form specified 
  1726. * by the parent function. WP_Error otherwise. 
  1727. * @param array $object_ids List of term object IDs. 
  1728. */ 
  1729. do_action( "delete_{$taxonomy}", $term, $tt_id, $deleted_term, $object_ids ); 
  1730.  
  1731. return true; 
  1732.  
  1733. /** 
  1734. * Deletes one existing category. 
  1735. * 
  1736. * @since 2.0.0 
  1737. * 
  1738. * @param int $cat_ID 
  1739. * @return bool|int|WP_Error Returns true if completes delete action; false if term doesn't exist; 
  1740. * Zero on attempted deletion of default Category; WP_Error object is also a possibility. 
  1741. */ 
  1742. function wp_delete_category( $cat_ID ) { 
  1743. return wp_delete_term( $cat_ID, 'category' ); 
  1744.  
  1745. /** 
  1746. * Retrieves the terms associated with the given object(s), in the supplied taxonomies. 
  1747. * 
  1748. * @since 2.3.0 
  1749. * @since 4.2.0 Added support for 'taxonomy', 'parent', and 'term_taxonomy_id' values of `$orderby`. 
  1750. * Introduced `$parent` argument. 
  1751. * @since 4.4.0 Introduced `$meta_query` and `$update_term_meta_cache` arguments. When `$fields` is 'all' or 
  1752. * 'all_with_object_id', an array of `WP_Term` objects will be returned. 
  1753. * @since 4.7.0 Refactored to use WP_Term_Query, and to support any WP_Term_Query arguments. 
  1754. * 
  1755. * @global wpdb $wpdb WordPress database abstraction object. 
  1756. * 
  1757. * @param int|array $object_ids The ID(s) of the object(s) to retrieve. 
  1758. * @param string|array $taxonomies The taxonomies to retrieve terms from. 
  1759. * @param array|string $args See WP_Term_Query::__construct() for supported arguments. 
  1760. * @return array|WP_Error The requested term data or empty array if no terms found. 
  1761. * WP_Error if any of the $taxonomies don't exist. 
  1762. */ 
  1763. function wp_get_object_terms($object_ids, $taxonomies, $args = array()) { 
  1764. global $wpdb; 
  1765.  
  1766. if ( empty( $object_ids ) || empty( $taxonomies ) ) 
  1767. return array(); 
  1768.  
  1769. if ( !is_array($taxonomies) ) 
  1770. $taxonomies = array($taxonomies); 
  1771.  
  1772. foreach ( $taxonomies as $taxonomy ) { 
  1773. if ( ! taxonomy_exists($taxonomy) ) 
  1774. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  1775.  
  1776. if ( !is_array($object_ids) ) 
  1777. $object_ids = array($object_ids); 
  1778. $object_ids = array_map('intval', $object_ids); 
  1779.  
  1780. $args = wp_parse_args( $args ); 
  1781.  
  1782. $args['taxonomy'] = $taxonomies; 
  1783. $args['object_ids'] = $object_ids; 
  1784.  
  1785. $terms = get_terms( $args ); 
  1786.  
  1787. /** 
  1788. * Filters the terms for a given object or objects. 
  1789. * 
  1790. * @since 4.2.0 
  1791. * 
  1792. * @param array $terms An array of terms for the given object or objects. 
  1793. * @param array $object_ids Array of object IDs for which `$terms` were retrieved. 
  1794. * @param array $taxonomies Array of taxonomies from which `$terms` were retrieved. 
  1795. * @param array $args An array of arguments for retrieving terms for the given 
  1796. * object(s). See wp_get_object_terms() for details. 
  1797. */ 
  1798. $terms = apply_filters( 'get_object_terms', $terms, $object_ids, $taxonomies, $args ); 
  1799.  
  1800. $object_ids = implode( ', ', $object_ids ); 
  1801. $taxonomies = "'" . implode( "', '", array_map( 'esc_sql', $taxonomies ) ) . "'"; 
  1802.  
  1803. /** 
  1804. * Filters the terms for a given object or objects. 
  1805. * 
  1806. * The `$taxonomies` parameter passed to this filter is formatted as a SQL fragment. The 
  1807. * {@see 'get_object_terms'} filter is recommended as an alternative. 
  1808. * 
  1809. * @since 2.8.0 
  1810. * 
  1811. * @param array $terms An array of terms for the given object or objects. 
  1812. * @param int|array $object_ids Object ID or array of IDs. 
  1813. * @param string $taxonomies SQL-formatted (comma-separated and quoted) list of taxonomy names. 
  1814. * @param array $args An array of arguments for retrieving terms for the given object(s). 
  1815. * See wp_get_object_terms() for details. 
  1816. */ 
  1817. return apply_filters( 'wp_get_object_terms', $terms, $object_ids, $taxonomies, $args ); 
  1818.  
  1819. /** 
  1820. * Add a new term to the database. 
  1821. * 
  1822. * A non-existent term is inserted in the following sequence: 
  1823. * 1. The term is added to the term table, then related to the taxonomy. 
  1824. * 2. If everything is correct, several actions are fired. 
  1825. * 3. The 'term_id_filter' is evaluated. 
  1826. * 4. The term cache is cleaned. 
  1827. * 5. Several more actions are fired. 
  1828. * 6. An array is returned containing the term_id and term_taxonomy_id. 
  1829. * 
  1830. * If the 'slug' argument is not empty, then it is checked to see if the term 
  1831. * is invalid. If it is not a valid, existing term, it is added and the term_id 
  1832. * is given. 
  1833. * 
  1834. * If the taxonomy is hierarchical, and the 'parent' argument is not empty,  
  1835. * the term is inserted and the term_id will be given. 
  1836. * 
  1837. * Error handling: 
  1838. * If $taxonomy does not exist or $term is empty,  
  1839. * a WP_Error object will be returned. 
  1840. * 
  1841. * If the term already exists on the same hierarchical level,  
  1842. * or the term slug and name are not unique, a WP_Error object will be returned. 
  1843. * 
  1844. * @global wpdb $wpdb WordPress database abstraction object. 
  1845. * 
  1846. * @since 2.3.0 
  1847. * 
  1848. * @param string $term The term to add or update. 
  1849. * @param string $taxonomy The taxonomy to which to add the term. 
  1850. * @param array|string $args { 
  1851. * Optional. Array or string of arguments for inserting a term. 
  1852. * 
  1853. * @type string $alias_of Slug of the term to make this term an alias of. 
  1854. * Default empty string. Accepts a term slug. 
  1855. * @type string $description The term description. Default empty string. 
  1856. * @type int $parent The id of the parent term. Default 0. 
  1857. * @type string $slug The term slug to use. Default empty string. 
  1858. * } 
  1859. * @return array|WP_Error An array containing the `term_id` and `term_taxonomy_id`,  
  1860. * WP_Error otherwise. 
  1861. */ 
  1862. function wp_insert_term( $term, $taxonomy, $args = array() ) { 
  1863. global $wpdb; 
  1864.  
  1865. if ( ! taxonomy_exists($taxonomy) ) { 
  1866. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  1867. /** 
  1868. * Filters a term before it is sanitized and inserted into the database. 
  1869. * 
  1870. * @since 3.0.0 
  1871. * 
  1872. * @param string $term The term to add or update. 
  1873. * @param string $taxonomy Taxonomy slug. 
  1874. */ 
  1875. $term = apply_filters( 'pre_insert_term', $term, $taxonomy ); 
  1876. if ( is_wp_error( $term ) ) { 
  1877. return $term; 
  1878. if ( is_int( $term ) && 0 == $term ) { 
  1879. return new WP_Error( 'invalid_term_id', __( 'Invalid term ID.' ) ); 
  1880. if ( '' == trim( $term ) ) { 
  1881. return new WP_Error( 'empty_term_name', __( 'A name is required for this term.' ) ); 
  1882. $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); 
  1883. $args = wp_parse_args( $args, $defaults ); 
  1884.  
  1885. if ( $args['parent'] > 0 && ! term_exists( (int) $args['parent'] ) ) { 
  1886. return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) ); 
  1887.  
  1888. $args['name'] = $term; 
  1889. $args['taxonomy'] = $taxonomy; 
  1890.  
  1891. // Coerce null description to strings, to avoid database errors. 
  1892. $args['description'] = (string) $args['description']; 
  1893.  
  1894. $args = sanitize_term($args, $taxonomy, 'db'); 
  1895.  
  1896. // expected_slashed ($name) 
  1897. $name = wp_unslash( $args['name'] ); 
  1898. $description = wp_unslash( $args['description'] ); 
  1899. $parent = (int) $args['parent']; 
  1900.  
  1901. $slug_provided = ! empty( $args['slug'] ); 
  1902. if ( ! $slug_provided ) { 
  1903. $slug = sanitize_title( $name ); 
  1904. } else { 
  1905. $slug = $args['slug']; 
  1906.  
  1907. $term_group = 0; 
  1908. if ( $args['alias_of'] ) { 
  1909. $alias = get_term_by( 'slug', $args['alias_of'], $taxonomy ); 
  1910. if ( ! empty( $alias->term_group ) ) { 
  1911. // The alias we want is already in a group, so let's use that one. 
  1912. $term_group = $alias->term_group; 
  1913. } elseif ( ! empty( $alias->term_id ) ) { 
  1914. /** 
  1915. * The alias is not in a group, so we create a new one 
  1916. * and add the alias to it. 
  1917. */ 
  1918. $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1; 
  1919.  
  1920. wp_update_term( $alias->term_id, $taxonomy, array( 
  1921. 'term_group' => $term_group,  
  1922. ) ); 
  1923.  
  1924. /** 
  1925. * Prevent the creation of terms with duplicate names at the same level of a taxonomy hierarchy,  
  1926. * unless a unique slug has been explicitly provided. 
  1927. */ 
  1928. $name_matches = get_terms( $taxonomy, array( 
  1929. 'name' => $name,  
  1930. 'hide_empty' => false,  
  1931. ) ); 
  1932.  
  1933. /** 
  1934. * The `name` match in `get_terms()` doesn't differentiate accented characters,  
  1935. * so we do a stricter comparison here. 
  1936. */ 
  1937. $name_match = null; 
  1938. if ( $name_matches ) { 
  1939. foreach ( $name_matches as $_match ) { 
  1940. if ( strtolower( $name ) === strtolower( $_match->name ) ) { 
  1941. $name_match = $_match; 
  1942. break; 
  1943.  
  1944. if ( $name_match ) { 
  1945. $slug_match = get_term_by( 'slug', $slug, $taxonomy ); 
  1946. if ( ! $slug_provided || $name_match->slug === $slug || $slug_match ) { 
  1947. if ( is_taxonomy_hierarchical( $taxonomy ) ) { 
  1948. $siblings = get_terms( $taxonomy, array( 'get' => 'all', 'parent' => $parent ) ); 
  1949.  
  1950. $existing_term = null; 
  1951. if ( $name_match->slug === $slug && in_array( $name, wp_list_pluck( $siblings, 'name' ) ) ) { 
  1952. $existing_term = $name_match; 
  1953. } elseif ( $slug_match && in_array( $slug, wp_list_pluck( $siblings, 'slug' ) ) ) { 
  1954. $existing_term = $slug_match; 
  1955.  
  1956. if ( $existing_term ) { 
  1957. return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $existing_term->term_id ); 
  1958. } else { 
  1959. return new WP_Error( 'term_exists', __( 'A term with the name provided already exists in this taxonomy.' ), $name_match->term_id ); 
  1960.  
  1961. $slug = wp_unique_term_slug( $slug, (object) $args ); 
  1962.  
  1963. $data = compact( 'name', 'slug', 'term_group' ); 
  1964.  
  1965. /** 
  1966. * Filters term data before it is inserted into the database. 
  1967. * 
  1968. * @since 4.7.0 
  1969. * 
  1970. * @param array $data Term data to be inserted. 
  1971. * @param string $taxonomy Taxonomy slug. 
  1972. * @param array $args Arguments passed to wp_insert_term(). 
  1973. */ 
  1974. $data = apply_filters( 'wp_insert_term_data', $data, $taxonomy, $args ); 
  1975.  
  1976. if ( false === $wpdb->insert( $wpdb->terms, $data ) ) { 
  1977. return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error ); 
  1978.  
  1979. $term_id = (int) $wpdb->insert_id; 
  1980.  
  1981. // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string. 
  1982. if ( empty($slug) ) { 
  1983. $slug = sanitize_title($slug, $term_id); 
  1984.  
  1985. /** This action is documented in wp-includes/taxonomy.php */ 
  1986. do_action( 'edit_terms', $term_id, $taxonomy ); 
  1987. $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) ); 
  1988.  
  1989. /** This action is documented in wp-includes/taxonomy.php */ 
  1990. do_action( 'edited_terms', $term_id, $taxonomy ); 
  1991.  
  1992. $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id ) ); 
  1993.  
  1994. if ( !empty($tt_id) ) { 
  1995. return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 
  1996. $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) ); 
  1997. $tt_id = (int) $wpdb->insert_id; 
  1998.  
  1999. /** 
  2000. * Sanity check: if we just created a term with the same parent + taxonomy + slug but a higher term_id than 
  2001. * an existing term, then we have unwittingly created a duplicate term. Delete the dupe, and use the term_id 
  2002. * and term_taxonomy_id of the older term instead. Then return out of the function so that the "create" hooks 
  2003. * are not fired. 
  2004. */ 
  2005. $duplicate_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.term_id, tt.term_taxonomy_id FROM $wpdb->terms t INNER JOIN $wpdb->term_taxonomy tt ON ( tt.term_id = t.term_id ) WHERE t.slug = %s AND tt.parent = %d AND tt.taxonomy = %s AND t.term_id < %d AND tt.term_taxonomy_id != %d", $slug, $parent, $taxonomy, $term_id, $tt_id ) ); 
  2006. if ( $duplicate_term ) { 
  2007. $wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ) ); 
  2008. $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) ); 
  2009.  
  2010. $term_id = (int) $duplicate_term->term_id; 
  2011. $tt_id = (int) $duplicate_term->term_taxonomy_id; 
  2012.  
  2013. clean_term_cache( $term_id, $taxonomy ); 
  2014. return array( 'term_id' => $term_id, 'term_taxonomy_id' => $tt_id ); 
  2015.  
  2016. /** 
  2017. * Fires immediately after a new term is created, before the term cache is cleaned. 
  2018. * 
  2019. * @since 2.3.0 
  2020. * 
  2021. * @param int $term_id Term ID. 
  2022. * @param int $tt_id Term taxonomy ID. 
  2023. * @param string $taxonomy Taxonomy slug. 
  2024. */ 
  2025. do_action( "create_term", $term_id, $tt_id, $taxonomy ); 
  2026.  
  2027. /** 
  2028. * Fires after a new term is created for a specific taxonomy. 
  2029. * 
  2030. * The dynamic portion of the hook name, `$taxonomy`, refers 
  2031. * to the slug of the taxonomy the term was created for. 
  2032. * 
  2033. * @since 2.3.0 
  2034. * 
  2035. * @param int $term_id Term ID. 
  2036. * @param int $tt_id Term taxonomy ID. 
  2037. */ 
  2038. do_action( "create_{$taxonomy}", $term_id, $tt_id ); 
  2039.  
  2040. /** 
  2041. * Filters the term ID after a new term is created. 
  2042. * 
  2043. * @since 2.3.0 
  2044. * 
  2045. * @param int $term_id Term ID. 
  2046. * @param int $tt_id Taxonomy term ID. 
  2047. */ 
  2048. $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id ); 
  2049.  
  2050. clean_term_cache($term_id, $taxonomy); 
  2051.  
  2052. /** 
  2053. * Fires after a new term is created, and after the term cache has been cleaned. 
  2054. * 
  2055. * @since 2.3.0 
  2056. * 
  2057. * @param int $term_id Term ID. 
  2058. * @param int $tt_id Term taxonomy ID. 
  2059. * @param string $taxonomy Taxonomy slug. 
  2060. */ 
  2061. do_action( 'created_term', $term_id, $tt_id, $taxonomy ); 
  2062.  
  2063. /** 
  2064. * Fires after a new term in a specific taxonomy is created, and after the term 
  2065. * cache has been cleaned. 
  2066. * 
  2067. * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. 
  2068. * 
  2069. * @since 2.3.0 
  2070. * 
  2071. * @param int $term_id Term ID. 
  2072. * @param int $tt_id Term taxonomy ID. 
  2073. */ 
  2074. do_action( "created_{$taxonomy}", $term_id, $tt_id ); 
  2075.  
  2076. return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 
  2077.  
  2078. /** 
  2079. * Create Term and Taxonomy Relationships. 
  2080. * 
  2081. * Relates an object (post, link etc) to a term and taxonomy type. Creates the 
  2082. * term and taxonomy relationship if it doesn't already exist. Creates a term if 
  2083. * it doesn't exist (using the slug). 
  2084. * 
  2085. * A relationship means that the term is grouped in or belongs to the taxonomy. 
  2086. * A term has no meaning until it is given context by defining which taxonomy it 
  2087. * exists under. 
  2088. * 
  2089. * @since 2.3.0 
  2090. * 
  2091. * @global wpdb $wpdb The WordPress database abstraction object. 
  2092. * 
  2093. * @param int $object_id The object to relate to. 
  2094. * @param array|int|string $terms A single term slug, single term id, or array of either term slugs or ids. 
  2095. * Will replace all existing related terms in this taxonomy. 
  2096. * @param string $taxonomy The context in which to relate the term to the object. 
  2097. * @param bool $append Optional. If false will delete difference of terms. Default false. 
  2098. * @return array|WP_Error Term taxonomy IDs of the affected terms. 
  2099. */ 
  2100. function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) { 
  2101. global $wpdb; 
  2102.  
  2103. $object_id = (int) $object_id; 
  2104.  
  2105. if ( ! taxonomy_exists( $taxonomy ) ) { 
  2106. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  2107.  
  2108. if ( !is_array($terms) ) 
  2109. $terms = array($terms); 
  2110.  
  2111. if ( ! $append ) 
  2112. $old_tt_ids = wp_get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids', 'orderby' => 'none')); 
  2113. else 
  2114. $old_tt_ids = array(); 
  2115.  
  2116. $tt_ids = array(); 
  2117. $term_ids = array(); 
  2118. $new_tt_ids = array(); 
  2119.  
  2120. foreach ( (array) $terms as $term) { 
  2121. if ( !strlen(trim($term)) ) 
  2122. continue; 
  2123.  
  2124. if ( !$term_info = term_exists($term, $taxonomy) ) { 
  2125. // Skip if a non-existent term ID is passed. 
  2126. if ( is_int($term) ) 
  2127. continue; 
  2128. $term_info = wp_insert_term($term, $taxonomy); 
  2129. if ( is_wp_error($term_info) ) 
  2130. return $term_info; 
  2131. $term_ids[] = $term_info['term_id']; 
  2132. $tt_id = $term_info['term_taxonomy_id']; 
  2133. $tt_ids[] = $tt_id; 
  2134.  
  2135. if ( $wpdb->get_var( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id = %d", $object_id, $tt_id ) ) ) 
  2136. continue; 
  2137.  
  2138. /** 
  2139. * Fires immediately before an object-term relationship is added. 
  2140. * 
  2141. * @since 2.9.0 
  2142. * @since 4.7.0 Added the `$taxonomy` parameter. 
  2143. * 
  2144. * @param int $object_id Object ID. 
  2145. * @param int $tt_id Term taxonomy ID. 
  2146. * @param string $taxonomy Taxonomy slug. 
  2147. */ 
  2148. do_action( 'add_term_relationship', $object_id, $tt_id, $taxonomy ); 
  2149. $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $tt_id ) ); 
  2150.  
  2151. /** 
  2152. * Fires immediately after an object-term relationship is added. 
  2153. * 
  2154. * @since 2.9.0 
  2155. * @since 4.7.0 Added the `$taxonomy` parameter. 
  2156. * 
  2157. * @param int $object_id Object ID. 
  2158. * @param int $tt_id Term taxonomy ID. 
  2159. * @param string $taxonomy Taxonomy slug. 
  2160. */ 
  2161. do_action( 'added_term_relationship', $object_id, $tt_id, $taxonomy ); 
  2162. $new_tt_ids[] = $tt_id; 
  2163.  
  2164. if ( $new_tt_ids ) 
  2165. wp_update_term_count( $new_tt_ids, $taxonomy ); 
  2166.  
  2167. if ( ! $append ) { 
  2168. $delete_tt_ids = array_diff( $old_tt_ids, $tt_ids ); 
  2169.  
  2170. if ( $delete_tt_ids ) { 
  2171. $in_delete_tt_ids = "'" . implode( "', '", $delete_tt_ids ) . "'"; 
  2172. $delete_term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT tt.term_id FROM $wpdb->term_taxonomy AS tt WHERE tt.taxonomy = %s AND tt.term_taxonomy_id IN ($in_delete_tt_ids)", $taxonomy ) ); 
  2173. $delete_term_ids = array_map( 'intval', $delete_term_ids ); 
  2174.  
  2175. $remove = wp_remove_object_terms( $object_id, $delete_term_ids, $taxonomy ); 
  2176. if ( is_wp_error( $remove ) ) { 
  2177. return $remove; 
  2178.  
  2179. $t = get_taxonomy($taxonomy); 
  2180. if ( ! $append && isset($t->sort) && $t->sort ) { 
  2181. $values = array(); 
  2182. $term_order = 0; 
  2183. $final_tt_ids = wp_get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids')); 
  2184. foreach ( $tt_ids as $tt_id ) 
  2185. if ( in_array($tt_id, $final_tt_ids) ) 
  2186. $values[] = $wpdb->prepare( "(%d, %d, %d)", $object_id, $tt_id, ++$term_order); 
  2187. if ( $values ) 
  2188. if ( false === $wpdb->query( "INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id, term_order) VALUES " . join( ', ', $values ) . " ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)" ) ) 
  2189. return new WP_Error( 'db_insert_error', __( 'Could not insert term relationship into the database' ), $wpdb->last_error ); 
  2190.  
  2191. wp_cache_delete( $object_id, $taxonomy . '_relationships' ); 
  2192. wp_cache_delete( 'last_changed', 'terms' ); 
  2193.  
  2194. /** 
  2195. * Fires after an object's terms have been set. 
  2196. * 
  2197. * @since 2.8.0 
  2198. * 
  2199. * @param int $object_id Object ID. 
  2200. * @param array $terms An array of object terms. 
  2201. * @param array $tt_ids An array of term taxonomy IDs. 
  2202. * @param string $taxonomy Taxonomy slug. 
  2203. * @param bool $append Whether to append new terms to the old terms. 
  2204. * @param array $old_tt_ids Old array of term taxonomy IDs. 
  2205. */ 
  2206. do_action( 'set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ); 
  2207. return $tt_ids; 
  2208.  
  2209. /** 
  2210. * Add term(s) associated with a given object. 
  2211. * 
  2212. * @since 3.6.0 
  2213. * 
  2214. * @param int $object_id The ID of the object to which the terms will be added. 
  2215. * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to add. 
  2216. * @param array|string $taxonomy Taxonomy name. 
  2217. * @return array|WP_Error Term taxonomy IDs of the affected terms. 
  2218. */ 
  2219. function wp_add_object_terms( $object_id, $terms, $taxonomy ) { 
  2220. return wp_set_object_terms( $object_id, $terms, $taxonomy, true ); 
  2221.  
  2222. /** 
  2223. * Remove term(s) associated with a given object. 
  2224. * 
  2225. * @since 3.6.0 
  2226. * 
  2227. * @global wpdb $wpdb WordPress database abstraction object. 
  2228. * 
  2229. * @param int $object_id The ID of the object from which the terms will be removed. 
  2230. * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to remove. 
  2231. * @param array|string $taxonomy Taxonomy name. 
  2232. * @return bool|WP_Error True on success, false or WP_Error on failure. 
  2233. */ 
  2234. function wp_remove_object_terms( $object_id, $terms, $taxonomy ) { 
  2235. global $wpdb; 
  2236.  
  2237. $object_id = (int) $object_id; 
  2238.  
  2239. if ( ! taxonomy_exists( $taxonomy ) ) { 
  2240. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  2241.  
  2242. if ( ! is_array( $terms ) ) { 
  2243. $terms = array( $terms ); 
  2244.  
  2245. $tt_ids = array(); 
  2246.  
  2247. foreach ( (array) $terms as $term ) { 
  2248. if ( ! strlen( trim( $term ) ) ) { 
  2249. continue; 
  2250.  
  2251. if ( ! $term_info = term_exists( $term, $taxonomy ) ) { 
  2252. // Skip if a non-existent term ID is passed. 
  2253. if ( is_int( $term ) ) { 
  2254. continue; 
  2255.  
  2256. if ( is_wp_error( $term_info ) ) { 
  2257. return $term_info; 
  2258.  
  2259. $tt_ids[] = $term_info['term_taxonomy_id']; 
  2260.  
  2261. if ( $tt_ids ) { 
  2262. $in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'"; 
  2263.  
  2264. /** 
  2265. * Fires immediately before an object-term relationship is deleted. 
  2266. * 
  2267. * @since 2.9.0 
  2268. * @since 4.7.0 Added the `$taxonomy` parameter. 
  2269. * 
  2270. * @param int $object_id Object ID. 
  2271. * @param array $tt_ids An array of term taxonomy IDs. 
  2272. * @param string $taxonomy Taxonomy slug. 
  2273. */ 
  2274. do_action( 'delete_term_relationships', $object_id, $tt_ids, $taxonomy ); 
  2275. $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) ); 
  2276.  
  2277. wp_cache_delete( $object_id, $taxonomy . '_relationships' ); 
  2278. wp_cache_delete( 'last_changed', 'terms' ); 
  2279.  
  2280. /** 
  2281. * Fires immediately after an object-term relationship is deleted. 
  2282. * 
  2283. * @since 2.9.0 
  2284. * @since 4.7.0 Added the `$taxonomy` parameter. 
  2285. * 
  2286. * @param int $object_id Object ID. 
  2287. * @param array $tt_ids An array of term taxonomy IDs. 
  2288. * @param string $taxonomy Taxonomy slug. 
  2289. */ 
  2290. do_action( 'deleted_term_relationships', $object_id, $tt_ids, $taxonomy ); 
  2291.  
  2292. wp_update_term_count( $tt_ids, $taxonomy ); 
  2293.  
  2294. return (bool) $deleted; 
  2295.  
  2296. return false; 
  2297.  
  2298. /** 
  2299. * Will make slug unique, if it isn't already. 
  2300. * 
  2301. * The `$slug` has to be unique global to every taxonomy, meaning that one 
  2302. * taxonomy term can't have a matching slug with another taxonomy term. Each 
  2303. * slug has to be globally unique for every taxonomy. 
  2304. * 
  2305. * The way this works is that if the taxonomy that the term belongs to is 
  2306. * hierarchical and has a parent, it will append that parent to the $slug. 
  2307. * 
  2308. * If that still doesn't return an unique slug, then it try to append a number 
  2309. * until it finds a number that is truly unique. 
  2310. * 
  2311. * The only purpose for `$term` is for appending a parent, if one exists. 
  2312. * 
  2313. * @since 2.3.0 
  2314. * 
  2315. * @global wpdb $wpdb WordPress database abstraction object. 
  2316. * 
  2317. * @param string $slug The string that will be tried for a unique slug. 
  2318. * @param object $term The term object that the `$slug` will belong to. 
  2319. * @return string Will return a true unique slug. 
  2320. */ 
  2321. function wp_unique_term_slug( $slug, $term ) { 
  2322. global $wpdb; 
  2323.  
  2324. $needs_suffix = true; 
  2325. $original_slug = $slug; 
  2326.  
  2327. // As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies. 
  2328. if ( ! term_exists( $slug ) || get_option( 'db_version' ) >= 30133 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) { 
  2329. $needs_suffix = false; 
  2330.  
  2331. /** 
  2332. * If the taxonomy supports hierarchy and the term has a parent, make the slug unique 
  2333. * by incorporating parent slugs. 
  2334. */ 
  2335. $parent_suffix = ''; 
  2336. if ( $needs_suffix && is_taxonomy_hierarchical( $term->taxonomy ) && ! empty( $term->parent ) ) { 
  2337. $the_parent = $term->parent; 
  2338. while ( ! empty($the_parent) ) { 
  2339. $parent_term = get_term($the_parent, $term->taxonomy); 
  2340. if ( is_wp_error($parent_term) || empty($parent_term) ) 
  2341. break; 
  2342. $parent_suffix .= '-' . $parent_term->slug; 
  2343. if ( ! term_exists( $slug . $parent_suffix ) ) { 
  2344. break; 
  2345.  
  2346. if ( empty($parent_term->parent) ) 
  2347. break; 
  2348. $the_parent = $parent_term->parent; 
  2349.  
  2350. // If we didn't get a unique slug, try appending a number to make it unique. 
  2351.  
  2352. /** 
  2353. * Filters whether the proposed unique term slug is bad. 
  2354. * 
  2355. * @since 4.3.0 
  2356. * 
  2357. * @param bool $needs_suffix Whether the slug needs to be made unique with a suffix. 
  2358. * @param string $slug The slug. 
  2359. * @param object $term Term object. 
  2360. */ 
  2361. if ( apply_filters( 'wp_unique_term_slug_is_bad_slug', $needs_suffix, $slug, $term ) ) { 
  2362. if ( $parent_suffix ) { 
  2363. $slug .= $parent_suffix; 
  2364. } else { 
  2365. if ( ! empty( $term->term_id ) ) 
  2366. $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s AND term_id != %d", $slug, $term->term_id ); 
  2367. else 
  2368. $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $slug ); 
  2369.  
  2370. if ( $wpdb->get_var( $query ) ) { 
  2371. $num = 2; 
  2372. do { 
  2373. $alt_slug = $slug . "-$num"; 
  2374. $num++; 
  2375. $slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug ) ); 
  2376. } while ( $slug_check ); 
  2377. $slug = $alt_slug; 
  2378.  
  2379. /** 
  2380. * Filters the unique term slug. 
  2381. * 
  2382. * @since 4.3.0 
  2383. * 
  2384. * @param string $slug Unique term slug. 
  2385. * @param object $term Term object. 
  2386. * @param string $original_slug Slug originally passed to the function for testing. 
  2387. */ 
  2388. return apply_filters( 'wp_unique_term_slug', $slug, $term, $original_slug ); 
  2389.  
  2390. /** 
  2391. * Update term based on arguments provided. 
  2392. * 
  2393. * The $args will indiscriminately override all values with the same field name. 
  2394. * Care must be taken to not override important information need to update or 
  2395. * update will fail (or perhaps create a new term, neither would be acceptable). 
  2396. * 
  2397. * Defaults will set 'alias_of', 'description', 'parent', and 'slug' if not 
  2398. * defined in $args already. 
  2399. * 
  2400. * 'alias_of' will create a term group, if it doesn't already exist, and update 
  2401. * it for the $term. 
  2402. * 
  2403. * If the 'slug' argument in $args is missing, then the 'name' in $args will be 
  2404. * used. It should also be noted that if you set 'slug' and it isn't unique then 
  2405. * a WP_Error will be passed back. If you don't pass any slug, then a unique one 
  2406. * will be created for you. 
  2407. * 
  2408. * For what can be overrode in `$args`, check the term scheme can contain and stay 
  2409. * away from the term keys. 
  2410. * 
  2411. * @since 2.3.0 
  2412. * 
  2413. * @global wpdb $wpdb WordPress database abstraction object. 
  2414. * 
  2415. * @param int $term_id The ID of the term 
  2416. * @param string $taxonomy The context in which to relate the term to the object. 
  2417. * @param array|string $args Optional. Array of get_terms() arguments. Default empty array. 
  2418. * @return array|WP_Error Returns Term ID and Taxonomy Term ID 
  2419. */ 
  2420. function wp_update_term( $term_id, $taxonomy, $args = array() ) { 
  2421. global $wpdb; 
  2422.  
  2423. if ( ! taxonomy_exists( $taxonomy ) ) { 
  2424. return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); 
  2425.  
  2426. $term_id = (int) $term_id; 
  2427.  
  2428. // First, get all of the original args 
  2429. $term = get_term( $term_id, $taxonomy ); 
  2430.  
  2431. if ( is_wp_error( $term ) ) { 
  2432. return $term; 
  2433.  
  2434. if ( ! $term ) { 
  2435. return new WP_Error( 'invalid_term', __( 'Empty Term' ) ); 
  2436.  
  2437. $term = (array) $term->data; 
  2438.  
  2439. // Escape data pulled from DB. 
  2440. $term = wp_slash( $term ); 
  2441.  
  2442. // Merge old and new args with new args overwriting old ones. 
  2443. $args = array_merge($term, $args); 
  2444.  
  2445. $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); 
  2446. $args = wp_parse_args($args, $defaults); 
  2447. $args = sanitize_term($args, $taxonomy, 'db'); 
  2448. $parsed_args = $args; 
  2449.  
  2450. // expected_slashed ($name) 
  2451. $name = wp_unslash( $args['name'] ); 
  2452. $description = wp_unslash( $args['description'] ); 
  2453.  
  2454. $parsed_args['name'] = $name; 
  2455. $parsed_args['description'] = $description; 
  2456.  
  2457. if ( '' == trim( $name ) ) { 
  2458. return new WP_Error( 'empty_term_name', __( 'A name is required for this term.' ) ); 
  2459.  
  2460. if ( $parsed_args['parent'] > 0 && ! term_exists( (int) $parsed_args['parent'] ) ) { 
  2461. return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) ); 
  2462.  
  2463. $empty_slug = false; 
  2464. if ( empty( $args['slug'] ) ) { 
  2465. $empty_slug = true; 
  2466. $slug = sanitize_title($name); 
  2467. } else { 
  2468. $slug = $args['slug']; 
  2469.  
  2470. $parsed_args['slug'] = $slug; 
  2471.  
  2472. $term_group = isset( $parsed_args['term_group'] ) ? $parsed_args['term_group'] : 0; 
  2473. if ( $args['alias_of'] ) { 
  2474. $alias = get_term_by( 'slug', $args['alias_of'], $taxonomy ); 
  2475. if ( ! empty( $alias->term_group ) ) { 
  2476. // The alias we want is already in a group, so let's use that one. 
  2477. $term_group = $alias->term_group; 
  2478. } elseif ( ! empty( $alias->term_id ) ) { 
  2479. /** 
  2480. * The alias is not in a group, so we create a new one 
  2481. * and add the alias to it. 
  2482. */ 
  2483. $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1; 
  2484.  
  2485. wp_update_term( $alias->term_id, $taxonomy, array( 
  2486. 'term_group' => $term_group,  
  2487. ) ); 
  2488.  
  2489. $parsed_args['term_group'] = $term_group; 
  2490.  
  2491. /** 
  2492. * Filters the term parent. 
  2493. * 
  2494. * Hook to this filter to see if it will cause a hierarchy loop. 
  2495. * 
  2496. * @since 3.1.0 
  2497. * 
  2498. * @param int $parent ID of the parent term. 
  2499. * @param int $term_id Term ID. 
  2500. * @param string $taxonomy Taxonomy slug. 
  2501. * @param array $parsed_args An array of potentially altered update arguments for the given term. 
  2502. * @param array $args An array of update arguments for the given term. 
  2503. */ 
  2504. $parent = apply_filters( 'wp_update_term_parent', $args['parent'], $term_id, $taxonomy, $parsed_args, $args ); 
  2505.  
  2506. // Check for duplicate slug 
  2507. $duplicate = get_term_by( 'slug', $slug, $taxonomy ); 
  2508. if ( $duplicate && $duplicate->term_id != $term_id ) { 
  2509. // If an empty slug was passed or the parent changed, reset the slug to something unique. 
  2510. // Otherwise, bail. 
  2511. if ( $empty_slug || ( $parent != $term['parent']) ) { 
  2512. $slug = wp_unique_term_slug($slug, (object) $args); 
  2513. } else { 
  2514. /** translators: 1: Taxonomy term slug */ 
  2515. return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug)); 
  2516.  
  2517. $tt_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) ); 
  2518.  
  2519. // Check whether this is a shared term that needs splitting. 
  2520. $_term_id = _split_shared_term( $term_id, $tt_id ); 
  2521. if ( ! is_wp_error( $_term_id ) ) { 
  2522. $term_id = $_term_id; 
  2523.  
  2524. /** 
  2525. * Fires immediately before the given terms are edited. 
  2526. * 
  2527. * @since 2.9.0 
  2528. * 
  2529. * @param int $term_id Term ID. 
  2530. * @param string $taxonomy Taxonomy slug. 
  2531. */ 
  2532. do_action( 'edit_terms', $term_id, $taxonomy ); 
  2533.  
  2534. $data = compact( 'name', 'slug', 'term_group' ); 
  2535.  
  2536. /** 
  2537. * Filters term data before it is updated in the database. 
  2538. * 
  2539. * @since 4.7.0 
  2540. * 
  2541. * @param array $data Term data to be updated. 
  2542. * @param int $term_id Term ID. 
  2543. * @param string $taxonomy Taxonomy slug. 
  2544. * @param array $args Arguments passed to wp_update_term(). 
  2545. */ 
  2546. $data = apply_filters( 'wp_update_term_data', $data, $term_id, $taxonomy, $args ); 
  2547.  
  2548. $wpdb->update( $wpdb->terms, $data, compact( 'term_id' ) ); 
  2549. if ( empty($slug) ) { 
  2550. $slug = sanitize_title($name, $term_id); 
  2551. $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) ); 
  2552.  
  2553. /** 
  2554. * Fires immediately after the given terms are edited. 
  2555. * 
  2556. * @since 2.9.0 
  2557. * 
  2558. * @param int $term_id Term ID 
  2559. * @param string $taxonomy Taxonomy slug. 
  2560. */ 
  2561. do_action( 'edited_terms', $term_id, $taxonomy ); 
  2562.  
  2563. /** 
  2564. * Fires immediate before a term-taxonomy relationship is updated. 
  2565. * 
  2566. * @since 2.9.0 
  2567. * 
  2568. * @param int $tt_id Term taxonomy ID. 
  2569. * @param string $taxonomy Taxonomy slug. 
  2570. */ 
  2571. do_action( 'edit_term_taxonomy', $tt_id, $taxonomy ); 
  2572.  
  2573. $wpdb->update( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent' ), array( 'term_taxonomy_id' => $tt_id ) ); 
  2574.  
  2575. /** 
  2576. * Fires immediately after a term-taxonomy relationship is updated. 
  2577. * 
  2578. * @since 2.9.0 
  2579. * 
  2580. * @param int $tt_id Term taxonomy ID. 
  2581. * @param string $taxonomy Taxonomy slug. 
  2582. */ 
  2583. do_action( 'edited_term_taxonomy', $tt_id, $taxonomy ); 
  2584.  
  2585. /** 
  2586. * Fires after a term has been updated, but before the term cache has been cleaned. 
  2587. * 
  2588. * @since 2.3.0 
  2589. * 
  2590. * @param int $term_id Term ID. 
  2591. * @param int $tt_id Term taxonomy ID. 
  2592. * @param string $taxonomy Taxonomy slug. 
  2593. */ 
  2594. do_action( "edit_term", $term_id, $tt_id, $taxonomy ); 
  2595.  
  2596. /** 
  2597. * Fires after a term in a specific taxonomy has been updated, but before the term 
  2598. * cache has been cleaned. 
  2599. * 
  2600. * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. 
  2601. * 
  2602. * @since 2.3.0 
  2603. * 
  2604. * @param int $term_id Term ID. 
  2605. * @param int $tt_id Term taxonomy ID. 
  2606. */ 
  2607. do_action( "edit_{$taxonomy}", $term_id, $tt_id ); 
  2608.  
  2609. /** This filter is documented in wp-includes/taxonomy.php */ 
  2610. $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id ); 
  2611.  
  2612. clean_term_cache($term_id, $taxonomy); 
  2613.  
  2614. /** 
  2615. * Fires after a term has been updated, and the term cache has been cleaned. 
  2616. * 
  2617. * @since 2.3.0 
  2618. * 
  2619. * @param int $term_id Term ID. 
  2620. * @param int $tt_id Term taxonomy ID. 
  2621. * @param string $taxonomy Taxonomy slug. 
  2622. */ 
  2623. do_action( "edited_term", $term_id, $tt_id, $taxonomy ); 
  2624.  
  2625. /** 
  2626. * Fires after a term for a specific taxonomy has been updated, and the term 
  2627. * cache has been cleaned. 
  2628. * 
  2629. * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. 
  2630. * 
  2631. * @since 2.3.0 
  2632. * 
  2633. * @param int $term_id Term ID. 
  2634. * @param int $tt_id Term taxonomy ID. 
  2635. */ 
  2636. do_action( "edited_{$taxonomy}", $term_id, $tt_id ); 
  2637.  
  2638. return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 
  2639.  
  2640. /** 
  2641. * Enable or disable term counting. 
  2642. * 
  2643. * @since 2.5.0 
  2644. * 
  2645. * @staticvar bool $_defer 
  2646. * 
  2647. * @param bool $defer Optional. Enable if true, disable if false. 
  2648. * @return bool Whether term counting is enabled or disabled. 
  2649. */ 
  2650. function wp_defer_term_counting($defer=null) { 
  2651. static $_defer = false; 
  2652.  
  2653. if ( is_bool($defer) ) { 
  2654. $_defer = $defer; 
  2655. // flush any deferred counts 
  2656. if ( !$defer ) 
  2657. wp_update_term_count( null, null, true ); 
  2658.  
  2659. return $_defer; 
  2660.  
  2661. /** 
  2662. * Updates the amount of terms in taxonomy. 
  2663. * 
  2664. * If there is a taxonomy callback applied, then it will be called for updating 
  2665. * the count. 
  2666. * 
  2667. * The default action is to count what the amount of terms have the relationship 
  2668. * of term ID. Once that is done, then update the database. 
  2669. * 
  2670. * @since 2.3.0 
  2671. * 
  2672. * @staticvar array $_deferred 
  2673. * 
  2674. * @param int|array $terms The term_taxonomy_id of the terms. 
  2675. * @param string $taxonomy The context of the term. 
  2676. * @param bool $do_deferred Whether to flush the deferred term counts too. Default false. 
  2677. * @return bool If no terms will return false, and if successful will return true. 
  2678. */ 
  2679. function wp_update_term_count( $terms, $taxonomy, $do_deferred = false ) { 
  2680. static $_deferred = array(); 
  2681.  
  2682. if ( $do_deferred ) { 
  2683. foreach ( (array) array_keys($_deferred) as $tax ) { 
  2684. wp_update_term_count_now( $_deferred[$tax], $tax ); 
  2685. unset( $_deferred[$tax] ); 
  2686.  
  2687. if ( empty($terms) ) 
  2688. return false; 
  2689.  
  2690. if ( !is_array($terms) ) 
  2691. $terms = array($terms); 
  2692.  
  2693. if ( wp_defer_term_counting() ) { 
  2694. if ( !isset($_deferred[$taxonomy]) ) 
  2695. $_deferred[$taxonomy] = array(); 
  2696. $_deferred[$taxonomy] = array_unique( array_merge($_deferred[$taxonomy], $terms) ); 
  2697. return true; 
  2698.  
  2699. return wp_update_term_count_now( $terms, $taxonomy ); 
  2700.  
  2701. /** 
  2702. * Perform term count update immediately. 
  2703. * 
  2704. * @since 2.5.0 
  2705. * 
  2706. * @param array $terms The term_taxonomy_id of terms to update. 
  2707. * @param string $taxonomy The context of the term. 
  2708. * @return true Always true when complete. 
  2709. */ 
  2710. function wp_update_term_count_now( $terms, $taxonomy ) { 
  2711. $terms = array_map('intval', $terms); 
  2712.  
  2713. $taxonomy = get_taxonomy($taxonomy); 
  2714. if ( !empty($taxonomy->update_count_callback) ) { 
  2715. call_user_func($taxonomy->update_count_callback, $terms, $taxonomy); 
  2716. } else { 
  2717. $object_types = (array) $taxonomy->object_type; 
  2718. foreach ( $object_types as &$object_type ) { 
  2719. if ( 0 === strpos( $object_type, 'attachment:' ) ) 
  2720. list( $object_type ) = explode( ':', $object_type ); 
  2721.  
  2722. if ( $object_types == array_filter( $object_types, 'post_type_exists' ) ) { 
  2723. // Only post types are attached to this taxonomy 
  2724. _update_post_term_count( $terms, $taxonomy ); 
  2725. } else { 
  2726. // Default count updater 
  2727. _update_generic_term_count( $terms, $taxonomy ); 
  2728.  
  2729. clean_term_cache($terms, '', false); 
  2730.  
  2731. return true; 
  2732.  
  2733. // 
  2734.  // Cache 
  2735. // 
  2736.   
  2737. /** 
  2738. * Removes the taxonomy relationship to terms from the cache. 
  2739. * 
  2740. * Will remove the entire taxonomy relationship containing term `$object_id`. The 
  2741. * term IDs have to exist within the taxonomy `$object_type` for the deletion to 
  2742. * take place. 
  2743. * 
  2744. * @since 2.3.0 
  2745. * 
  2746. * @global bool $_wp_suspend_cache_invalidation 
  2747. * 
  2748. * @see get_object_taxonomies() for more on $object_type. 
  2749. * 
  2750. * @param int|array $object_ids Single or list of term object ID(s). 
  2751. * @param array|string $object_type The taxonomy object type. 
  2752. */ 
  2753. function clean_object_term_cache($object_ids, $object_type) { 
  2754. global $_wp_suspend_cache_invalidation; 
  2755.  
  2756. if ( ! empty( $_wp_suspend_cache_invalidation ) ) { 
  2757. return; 
  2758.  
  2759. if ( !is_array($object_ids) ) 
  2760. $object_ids = array($object_ids); 
  2761.  
  2762. $taxonomies = get_object_taxonomies( $object_type ); 
  2763.  
  2764. foreach ( $object_ids as $id ) { 
  2765. foreach ( $taxonomies as $taxonomy ) { 
  2766. wp_cache_delete($id, "{$taxonomy}_relationships"); 
  2767.  
  2768. /** 
  2769. * Fires after the object term cache has been cleaned. 
  2770. * 
  2771. * @since 2.5.0 
  2772. * 
  2773. * @param array $object_ids An array of object IDs. 
  2774. * @param string $objet_type Object type. 
  2775. */ 
  2776. do_action( 'clean_object_term_cache', $object_ids, $object_type ); 
  2777.  
  2778. /** 
  2779. * Will remove all of the term ids from the cache. 
  2780. * 
  2781. * @since 2.3.0 
  2782. * 
  2783. * @global wpdb $wpdb WordPress database abstraction object. 
  2784. * @global bool $_wp_suspend_cache_invalidation 
  2785. * 
  2786. * @param int|array $ids Single or list of Term IDs. 
  2787. * @param string $taxonomy Optional. Can be empty and will assume `tt_ids`, else will use for context. 
  2788. * Default empty. 
  2789. * @param bool $clean_taxonomy Optional. Whether to clean taxonomy wide caches (true), or just individual 
  2790. * term object caches (false). Default true. 
  2791. */ 
  2792. function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) { 
  2793. global $wpdb, $_wp_suspend_cache_invalidation; 
  2794.  
  2795. if ( ! empty( $_wp_suspend_cache_invalidation ) ) { 
  2796. return; 
  2797.  
  2798. if ( !is_array($ids) ) 
  2799. $ids = array($ids); 
  2800.  
  2801. $taxonomies = array(); 
  2802. // If no taxonomy, assume tt_ids. 
  2803. if ( empty($taxonomy) ) { 
  2804. $tt_ids = array_map('intval', $ids); 
  2805. $tt_ids = implode(', ', $tt_ids); 
  2806. $terms = $wpdb->get_results("SELECT term_id, taxonomy FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ($tt_ids)"); 
  2807. $ids = array(); 
  2808. foreach ( (array) $terms as $term ) { 
  2809. $taxonomies[] = $term->taxonomy; 
  2810. $ids[] = $term->term_id; 
  2811. wp_cache_delete( $term->term_id, 'terms' ); 
  2812. $taxonomies = array_unique($taxonomies); 
  2813. } else { 
  2814. $taxonomies = array($taxonomy); 
  2815. foreach ( $taxonomies as $taxonomy ) { 
  2816. foreach ( $ids as $id ) { 
  2817. wp_cache_delete( $id, 'terms' ); 
  2818.  
  2819. foreach ( $taxonomies as $taxonomy ) { 
  2820. if ( $clean_taxonomy ) { 
  2821. wp_cache_delete('all_ids', $taxonomy); 
  2822. wp_cache_delete('get', $taxonomy); 
  2823. delete_option("{$taxonomy}_children"); 
  2824. // Regenerate {$taxonomy}_children 
  2825. _get_term_hierarchy($taxonomy); 
  2826.  
  2827. /** 
  2828. * Fires once after each taxonomy's term cache has been cleaned. 
  2829. * 
  2830. * @since 2.5.0 
  2831. * @since 4.5.0 Added the `$clean_taxonomy` parameter. 
  2832. * 
  2833. * @param array $ids An array of term IDs. 
  2834. * @param string $taxonomy Taxonomy slug. 
  2835. * @param bool $clean_taxonomy Whether or not to clean taxonomy-wide caches 
  2836. */ 
  2837. do_action( 'clean_term_cache', $ids, $taxonomy, $clean_taxonomy ); 
  2838.  
  2839. wp_cache_set( 'last_changed', microtime(), 'terms' ); 
  2840.  
  2841. /** 
  2842. * Retrieves the taxonomy relationship to the term object id. 
  2843. * 
  2844. * Upstream functions (like get_the_terms() and is_object_in_term()) are 
  2845. * responsible for populating the object-term relationship cache. The current 
  2846. * function only fetches relationship data that is already in the cache. 
  2847. * 
  2848. * @since 2.3.0 
  2849. * @since 4.7.0 Returns a WP_Error object if get_term() returns an error for 
  2850. * any of the matched terms. 
  2851. * 
  2852. * @param int $id Term object ID. 
  2853. * @param string $taxonomy Taxonomy name. 
  2854. * @return bool|array|WP_Error Array of `WP_Term` objects, if cached. 
  2855. * False if cache is empty for `$taxonomy` and `$id`. 
  2856. * WP_Error if get_term() returns an error object for any term. 
  2857. */ 
  2858. function get_object_term_cache( $id, $taxonomy ) { 
  2859. $_term_ids = wp_cache_get( $id, "{$taxonomy}_relationships" ); 
  2860.  
  2861. // We leave the priming of relationship caches to upstream functions. 
  2862. if ( false === $_term_ids ) { 
  2863. return false; 
  2864.  
  2865. // Backward compatibility for if a plugin is putting objects into the cache, rather than IDs. 
  2866. $term_ids = array(); 
  2867. foreach ( $_term_ids as $term_id ) { 
  2868. if ( is_numeric( $term_id ) ) { 
  2869. $term_ids[] = intval( $term_id ); 
  2870. } elseif ( isset( $term_id->term_id ) ) { 
  2871. $term_ids[] = intval( $term_id->term_id ); 
  2872.  
  2873. // Fill the term objects. 
  2874. _prime_term_caches( $term_ids ); 
  2875.  
  2876. $terms = array(); 
  2877. foreach ( $term_ids as $term_id ) { 
  2878. $term = get_term( $term_id, $taxonomy ); 
  2879. if ( is_wp_error( $term ) ) { 
  2880. return $term; 
  2881.  
  2882. $terms[] = $term; 
  2883.  
  2884. return $terms; 
  2885.  
  2886. /** 
  2887. * Updates the cache for the given term object ID(s). 
  2888. * 
  2889. * Note: Due to performance concerns, great care should be taken to only update 
  2890. * term caches when necessary. Processing time can increase exponentially depending 
  2891. * on both the number of passed term IDs and the number of taxonomies those terms 
  2892. * belong to. 
  2893. * 
  2894. * Caches will only be updated for terms not already cached. 
  2895. * 
  2896. * @since 2.3.0 
  2897. * 
  2898. * @param string|array $object_ids Comma-separated list or array of term object IDs. 
  2899. * @param array|string $object_type The taxonomy object type. 
  2900. * @return void|false False if all of the terms in `$object_ids` are already cached. 
  2901. */ 
  2902. function update_object_term_cache($object_ids, $object_type) { 
  2903. if ( empty($object_ids) ) 
  2904. return; 
  2905.  
  2906. if ( !is_array($object_ids) ) 
  2907. $object_ids = explode(', ', $object_ids); 
  2908.  
  2909. $object_ids = array_map('intval', $object_ids); 
  2910.  
  2911. $taxonomies = get_object_taxonomies($object_type); 
  2912.  
  2913. $ids = array(); 
  2914. foreach ( (array) $object_ids as $id ) { 
  2915. foreach ( $taxonomies as $taxonomy ) { 
  2916. if ( false === wp_cache_get($id, "{$taxonomy}_relationships") ) { 
  2917. $ids[] = $id; 
  2918. break; 
  2919.  
  2920. if ( empty( $ids ) ) 
  2921. return false; 
  2922.  
  2923. $terms = wp_get_object_terms( $ids, $taxonomies, array( 
  2924. 'fields' => 'all_with_object_id',  
  2925. 'orderby' => 'name',  
  2926. 'update_term_meta_cache' => false,  
  2927. ) ); 
  2928.  
  2929. $object_terms = array(); 
  2930. foreach ( (array) $terms as $term ) { 
  2931. $object_terms[ $term->object_id ][ $term->taxonomy ][] = $term->term_id; 
  2932.  
  2933. foreach ( $ids as $id ) { 
  2934. foreach ( $taxonomies as $taxonomy ) { 
  2935. if ( ! isset($object_terms[$id][$taxonomy]) ) { 
  2936. if ( !isset($object_terms[$id]) ) 
  2937. $object_terms[$id] = array(); 
  2938. $object_terms[$id][$taxonomy] = array(); 
  2939.  
  2940. foreach ( $object_terms as $id => $value ) { 
  2941. foreach ( $value as $taxonomy => $terms ) { 
  2942. wp_cache_add( $id, $terms, "{$taxonomy}_relationships" ); 
  2943.  
  2944. /** 
  2945. * Updates Terms to Taxonomy in cache. 
  2946. * 
  2947. * @since 2.3.0 
  2948. * 
  2949. * @param array $terms List of term objects to change. 
  2950. * @param string $taxonomy Optional. Update Term to this taxonomy in cache. Default empty. 
  2951. */ 
  2952. function update_term_cache( $terms, $taxonomy = '' ) { 
  2953. foreach ( (array) $terms as $term ) { 
  2954. // Create a copy in case the array was passed by reference. 
  2955. $_term = clone $term; 
  2956.  
  2957. // Object ID should not be cached. 
  2958. unset( $_term->object_id ); 
  2959.  
  2960. wp_cache_add( $term->term_id, $_term, 'terms' ); 
  2961.  
  2962. // 
  2963.  // Private 
  2964. // 
  2965.   
  2966. /** 
  2967. * Retrieves children of taxonomy as Term IDs. 
  2968. * 
  2969. * @ignore 
  2970. * @since 2.3.0 
  2971. * 
  2972. * @param string $taxonomy Taxonomy name. 
  2973. * @return array Empty if $taxonomy isn't hierarchical or returns children as Term IDs. 
  2974. */ 
  2975. function _get_term_hierarchy( $taxonomy ) { 
  2976. if ( !is_taxonomy_hierarchical($taxonomy) ) 
  2977. return array(); 
  2978. $children = get_option("{$taxonomy}_children"); 
  2979.  
  2980. if ( is_array($children) ) 
  2981. return $children; 
  2982. $children = array(); 
  2983. $terms = get_terms($taxonomy, array('get' => 'all', 'orderby' => 'id', 'fields' => 'id=>parent')); 
  2984. foreach ( $terms as $term_id => $parent ) { 
  2985. if ( $parent > 0 ) 
  2986. $children[$parent][] = $term_id; 
  2987. update_option("{$taxonomy}_children", $children); 
  2988.  
  2989. return $children; 
  2990.  
  2991. /** 
  2992. * Get the subset of $terms that are descendants of $term_id. 
  2993. * 
  2994. * If `$terms` is an array of objects, then _get_term_children() returns an array of objects. 
  2995. * If `$terms` is an array of IDs, then _get_term_children() returns an array of IDs. 
  2996. * 
  2997. * @access private 
  2998. * @since 2.3.0 
  2999. * 
  3000. * @param int $term_id The ancestor term: all returned terms should be descendants of `$term_id`. 
  3001. * @param array $terms The set of terms - either an array of term objects or term IDs - from which those that 
  3002. * are descendants of $term_id will be chosen. 
  3003. * @param string $taxonomy The taxonomy which determines the hierarchy of the terms. 
  3004. * @param array $ancestors Optional. Term ancestors that have already been identified. Passed by reference, to keep 
  3005. * track of found terms when recursing the hierarchy. The array of located ancestors is used 
  3006. * to prevent infinite recursion loops. For performance, `term_ids` are used as array keys,  
  3007. * with 1 as value. Default empty array. 
  3008. * @return array|WP_Error The subset of $terms that are descendants of $term_id. 
  3009. */ 
  3010. function _get_term_children( $term_id, $terms, $taxonomy, &$ancestors = array() ) { 
  3011. $empty_array = array(); 
  3012. if ( empty($terms) ) 
  3013. return $empty_array; 
  3014.  
  3015. $term_list = array(); 
  3016. $has_children = _get_term_hierarchy($taxonomy); 
  3017.  
  3018. if ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) ) 
  3019. return $empty_array; 
  3020.  
  3021. // Include the term itself in the ancestors array, so we can properly detect when a loop has occurred. 
  3022. if ( empty( $ancestors ) ) { 
  3023. $ancestors[ $term_id ] = 1; 
  3024.  
  3025. foreach ( (array) $terms as $term ) { 
  3026. $use_id = false; 
  3027. if ( !is_object($term) ) { 
  3028. $term = get_term($term, $taxonomy); 
  3029. if ( is_wp_error( $term ) ) 
  3030. return $term; 
  3031. $use_id = true; 
  3032.  
  3033. // Don't recurse if we've already identified the term as a child - this indicates a loop. 
  3034. if ( isset( $ancestors[ $term->term_id ] ) ) { 
  3035. continue; 
  3036.  
  3037. if ( $term->parent == $term_id ) { 
  3038. if ( $use_id ) 
  3039. $term_list[] = $term->term_id; 
  3040. else 
  3041. $term_list[] = $term; 
  3042.  
  3043. if ( !isset($has_children[$term->term_id]) ) 
  3044. continue; 
  3045.  
  3046. $ancestors[ $term->term_id ] = 1; 
  3047.  
  3048. if ( $children = _get_term_children( $term->term_id, $terms, $taxonomy, $ancestors) ) 
  3049. $term_list = array_merge($term_list, $children); 
  3050.  
  3051. return $term_list; 
  3052.  
  3053. /** 
  3054. * Add count of children to parent count. 
  3055. * 
  3056. * Recalculates term counts by including items from child terms. Assumes all 
  3057. * relevant children are already in the $terms argument. 
  3058. * 
  3059. * @access private 
  3060. * @since 2.3.0 
  3061. * 
  3062. * @global wpdb $wpdb WordPress database abstraction object. 
  3063. * 
  3064. * @param array $terms List of term objects, passed by reference. 
  3065. * @param string $taxonomy Term context. 
  3066. */ 
  3067. function _pad_term_counts( &$terms, $taxonomy ) { 
  3068. global $wpdb; 
  3069.  
  3070. // This function only works for hierarchical taxonomies like post categories. 
  3071. if ( !is_taxonomy_hierarchical( $taxonomy ) ) 
  3072. return; 
  3073.  
  3074. $term_hier = _get_term_hierarchy($taxonomy); 
  3075.  
  3076. if ( empty($term_hier) ) 
  3077. return; 
  3078.  
  3079. $term_items = array(); 
  3080. $terms_by_id = array(); 
  3081. $term_ids = array(); 
  3082.  
  3083. foreach ( (array) $terms as $key => $term ) { 
  3084. $terms_by_id[$term->term_id] = & $terms[$key]; 
  3085. $term_ids[$term->term_taxonomy_id] = $term->term_id; 
  3086.  
  3087. // Get the object and term ids and stick them in a lookup table. 
  3088. $tax_obj = get_taxonomy($taxonomy); 
  3089. $object_types = esc_sql($tax_obj->object_type); 
  3090. $results = $wpdb->get_results("SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships INNER JOIN $wpdb->posts ON object_id = ID WHERE term_taxonomy_id IN (" . implode(', ', array_keys($term_ids)) . ") AND post_type IN ('" . implode("', '", $object_types) . "') AND post_status = 'publish'"); 
  3091. foreach ( $results as $row ) { 
  3092. $id = $term_ids[$row->term_taxonomy_id]; 
  3093. $term_items[$id][$row->object_id] = isset($term_items[$id][$row->object_id]) ? ++$term_items[$id][$row->object_id] : 1; 
  3094.  
  3095. // Touch every ancestor's lookup row for each post in each term. 
  3096. foreach ( $term_ids as $term_id ) { 
  3097. $child = $term_id; 
  3098. $ancestors = array(); 
  3099. while ( !empty( $terms_by_id[$child] ) && $parent = $terms_by_id[$child]->parent ) { 
  3100. $ancestors[] = $child; 
  3101. if ( !empty( $term_items[$term_id] ) ) 
  3102. foreach ( $term_items[$term_id] as $item_id => $touches ) { 
  3103. $term_items[$parent][$item_id] = isset($term_items[$parent][$item_id]) ? ++$term_items[$parent][$item_id]: 1; 
  3104. $child = $parent; 
  3105.  
  3106. if ( in_array( $parent, $ancestors ) ) { 
  3107. break; 
  3108.  
  3109. // Transfer the touched cells. 
  3110. foreach ( (array) $term_items as $id => $items ) 
  3111. if ( isset($terms_by_id[$id]) ) 
  3112. $terms_by_id[$id]->count = count($items); 
  3113.  
  3114. /** 
  3115. * Adds any terms from the given IDs to the cache that do not already exist in cache. 
  3116. * 
  3117. * @since 4.6.0 
  3118. * @access private 
  3119. * 
  3120. * @global wpdb $wpdb WordPress database abstraction object. 
  3121. * 
  3122. * @param array $term_ids Array of term IDs. 
  3123. * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true. 
  3124. */ 
  3125. function _prime_term_caches( $term_ids, $update_meta_cache = true ) { 
  3126. global $wpdb; 
  3127.  
  3128. $non_cached_ids = _get_non_cached_ids( $term_ids, 'terms' ); 
  3129. if ( ! empty( $non_cached_ids ) ) { 
  3130. $fresh_terms = $wpdb->get_results( sprintf( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE t.term_id IN (%s)", join( ", ", array_map( 'intval', $non_cached_ids ) ) ) ); 
  3131.  
  3132. update_term_cache( $fresh_terms, $update_meta_cache ); 
  3133.  
  3134. if ( $update_meta_cache ) { 
  3135. update_termmeta_cache( $non_cached_ids ); 
  3136.  
  3137. // 
  3138.  // Default callbacks 
  3139. // 
  3140.   
  3141. /** 
  3142. * Will update term count based on object types of the current taxonomy. 
  3143. * 
  3144. * Private function for the default callback for post_tag and category 
  3145. * taxonomies. 
  3146. * 
  3147. * @access private 
  3148. * @since 2.3.0 
  3149. * 
  3150. * @global wpdb $wpdb WordPress database abstraction object. 
  3151. * 
  3152. * @param array $terms List of Term taxonomy IDs. 
  3153. * @param object $taxonomy Current taxonomy object of terms. 
  3154. */ 
  3155. function _update_post_term_count( $terms, $taxonomy ) { 
  3156. global $wpdb; 
  3157.  
  3158. $object_types = (array) $taxonomy->object_type; 
  3159.  
  3160. foreach ( $object_types as &$object_type ) 
  3161. list( $object_type ) = explode( ':', $object_type ); 
  3162.  
  3163. $object_types = array_unique( $object_types ); 
  3164.  
  3165. if ( false !== ( $check_attachments = array_search( 'attachment', $object_types ) ) ) { 
  3166. unset( $object_types[ $check_attachments ] ); 
  3167. $check_attachments = true; 
  3168.  
  3169. if ( $object_types ) 
  3170. $object_types = esc_sql( array_filter( $object_types, 'post_type_exists' ) ); 
  3171.  
  3172. foreach ( (array) $terms as $term ) { 
  3173. $count = 0; 
  3174.  
  3175. // Attachments can be 'inherit' status, we need to base count off the parent's status if so. 
  3176. if ( $check_attachments ) 
  3177. $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts p1 WHERE p1.ID = $wpdb->term_relationships.object_id AND ( post_status = 'publish' OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) = 'publish' ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) ); 
  3178.  
  3179. if ( $object_types ) 
  3180. $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . implode("', '", $object_types ) . "') AND term_taxonomy_id = %d", $term ) ); 
  3181.  
  3182. /** This action is documented in wp-includes/taxonomy.php */ 
  3183. do_action( 'edit_term_taxonomy', $term, $taxonomy->name ); 
  3184. $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); 
  3185.  
  3186. /** This action is documented in wp-includes/taxonomy.php */ 
  3187. do_action( 'edited_term_taxonomy', $term, $taxonomy->name ); 
  3188.  
  3189. /** 
  3190. * Will update term count based on number of objects. 
  3191. * 
  3192. * Default callback for the 'link_category' taxonomy. 
  3193. * 
  3194. * @since 3.3.0 
  3195. * 
  3196. * @global wpdb $wpdb WordPress database abstraction object. 
  3197. * 
  3198. * @param array $terms List of term taxonomy IDs. 
  3199. * @param object $taxonomy Current taxonomy object of terms. 
  3200. */ 
  3201. function _update_generic_term_count( $terms, $taxonomy ) { 
  3202. global $wpdb; 
  3203.  
  3204. foreach ( (array) $terms as $term ) { 
  3205. $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term ) ); 
  3206.  
  3207. /** This action is documented in wp-includes/taxonomy.php */ 
  3208. do_action( 'edit_term_taxonomy', $term, $taxonomy->name ); 
  3209. $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); 
  3210.  
  3211. /** This action is documented in wp-includes/taxonomy.php */ 
  3212. do_action( 'edited_term_taxonomy', $term, $taxonomy->name ); 
  3213.  
  3214. /** 
  3215. * Create a new term for a term_taxonomy item that currently shares its term 
  3216. * with another term_taxonomy. 
  3217. * 
  3218. * @ignore 
  3219. * @since 4.2.0 
  3220. * @since 4.3.0 Introduced `$record` parameter. Also, `$term_id` and 
  3221. * `$term_taxonomy_id` can now accept objects. 
  3222. * 
  3223. * @global wpdb $wpdb WordPress database abstraction object. 
  3224. * 
  3225. * @param int|object $term_id ID of the shared term, or the shared term object. 
  3226. * @param int|object $term_taxonomy_id ID of the term_taxonomy item to receive a new term, or the term_taxonomy object 
  3227. * (corresponding to a row from the term_taxonomy table). 
  3228. * @param bool $record Whether to record data about the split term in the options table. The recording 
  3229. * process has the potential to be resource-intensive, so during batch operations 
  3230. * it can be beneficial to skip inline recording and do it just once, after the 
  3231. * batch is processed. Only set this to `false` if you know what you are doing. 
  3232. * Default: true. 
  3233. * @return int|WP_Error When the current term does not need to be split (or cannot be split on the current 
  3234. * database schema), `$term_id` is returned. When the term is successfully split, the 
  3235. * new term_id is returned. A WP_Error is returned for miscellaneous errors. 
  3236. */ 
  3237. function _split_shared_term( $term_id, $term_taxonomy_id, $record = true ) { 
  3238. global $wpdb; 
  3239.  
  3240. if ( is_object( $term_id ) ) { 
  3241. $shared_term = $term_id; 
  3242. $term_id = intval( $shared_term->term_id ); 
  3243.  
  3244. if ( is_object( $term_taxonomy_id ) ) { 
  3245. $term_taxonomy = $term_taxonomy_id; 
  3246. $term_taxonomy_id = intval( $term_taxonomy->term_taxonomy_id ); 
  3247.  
  3248. // If there are no shared term_taxonomy rows, there's nothing to do here. 
  3249. $shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) ); 
  3250.  
  3251. if ( ! $shared_tt_count ) { 
  3252. return $term_id; 
  3253.  
  3254. /** 
  3255. * Verify that the term_taxonomy_id passed to the function is actually associated with the term_id. 
  3256. * If there's a mismatch, it may mean that the term is already split. Return the actual term_id from the db. 
  3257. */ 
  3258. $check_term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) ); 
  3259. if ( $check_term_id != $term_id ) { 
  3260. return $check_term_id; 
  3261.  
  3262. // Pull up data about the currently shared slug, which we'll use to populate the new one. 
  3263. if ( empty( $shared_term ) ) { 
  3264. $shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.* FROM $wpdb->terms t WHERE t.term_id = %d", $term_id ) ); 
  3265.  
  3266. $new_term_data = array( 
  3267. 'name' => $shared_term->name,  
  3268. 'slug' => $shared_term->slug,  
  3269. 'term_group' => $shared_term->term_group,  
  3270. ); 
  3271.  
  3272. if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) { 
  3273. return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error ); 
  3274.  
  3275. $new_term_id = (int) $wpdb->insert_id; 
  3276.  
  3277. // Update the existing term_taxonomy to point to the newly created term. 
  3278. $wpdb->update( $wpdb->term_taxonomy,  
  3279. array( 'term_id' => $new_term_id ),  
  3280. array( 'term_taxonomy_id' => $term_taxonomy_id ) 
  3281. ); 
  3282.  
  3283. // Reassign child terms to the new parent. 
  3284. if ( empty( $term_taxonomy ) ) { 
  3285. $term_taxonomy = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) ); 
  3286.  
  3287. $children_tt_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE parent = %d AND taxonomy = %s", $term_id, $term_taxonomy->taxonomy ) ); 
  3288. if ( ! empty( $children_tt_ids ) ) { 
  3289. foreach ( $children_tt_ids as $child_tt_id ) { 
  3290. $wpdb->update( $wpdb->term_taxonomy,  
  3291. array( 'parent' => $new_term_id ),  
  3292. array( 'term_taxonomy_id' => $child_tt_id ) 
  3293. ); 
  3294. clean_term_cache( $term_id, $term_taxonomy->taxonomy ); 
  3295. } else { 
  3296. // If the term has no children, we must force its taxonomy cache to be rebuilt separately. 
  3297. clean_term_cache( $new_term_id, $term_taxonomy->taxonomy ); 
  3298.  
  3299. // Clean the cache for term taxonomies formerly shared with the current term. 
  3300. $shared_term_taxonomies = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); 
  3301. if ( $shared_term_taxonomies ) { 
  3302. foreach ( $shared_term_taxonomies as $shared_term_taxonomy ) { 
  3303. clean_term_cache( $term_id, $shared_term_taxonomy ); 
  3304.  
  3305. // Keep a record of term_ids that have been split, keyed by old term_id. See wp_get_split_term(). 
  3306. if ( $record ) { 
  3307. $split_term_data = get_option( '_split_terms', array() ); 
  3308. if ( ! isset( $split_term_data[ $term_id ] ) ) { 
  3309. $split_term_data[ $term_id ] = array(); 
  3310.  
  3311. $split_term_data[ $term_id ][ $term_taxonomy->taxonomy ] = $new_term_id; 
  3312. update_option( '_split_terms', $split_term_data ); 
  3313.  
  3314. // If we've just split the final shared term, set the "finished" flag. 
  3315. $shared_terms_exist = $wpdb->get_results( 
  3316. "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt 
  3317. LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id 
  3318. GROUP BY t.term_id 
  3319. HAVING term_tt_count > 1 
  3320. LIMIT 1" 
  3321. ); 
  3322. if ( ! $shared_terms_exist ) { 
  3323. update_option( 'finished_splitting_shared_terms', true ); 
  3324.  
  3325. /** 
  3326. * Fires after a previously shared taxonomy term is split into two separate terms. 
  3327. * 
  3328. * @since 4.2.0 
  3329. * 
  3330. * @param int $term_id ID of the formerly shared term. 
  3331. * @param int $new_term_id ID of the new term created for the $term_taxonomy_id. 
  3332. * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split. 
  3333. * @param string $taxonomy Taxonomy for the split term. 
  3334. */ 
  3335. do_action( 'split_shared_term', $term_id, $new_term_id, $term_taxonomy_id, $term_taxonomy->taxonomy ); 
  3336.  
  3337. return $new_term_id; 
  3338.  
  3339. /** 
  3340. * Splits a batch of shared taxonomy terms. 
  3341. * 
  3342. * @since 4.3.0 
  3343. * 
  3344. * @global wpdb $wpdb WordPress database abstraction object. 
  3345. */ 
  3346. function _wp_batch_split_terms() { 
  3347. global $wpdb; 
  3348.  
  3349. $lock_name = 'term_split.lock'; 
  3350.  
  3351. // Try to lock. 
  3352. $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) ); 
  3353.  
  3354. if ( ! $lock_result ) { 
  3355. $lock_result = get_option( $lock_name ); 
  3356.  
  3357. // Bail if we were unable to create a lock, or if the existing lock is still valid. 
  3358. if ( ! $lock_result || ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) ) { 
  3359. wp_schedule_single_event( time() + ( 5 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' ); 
  3360. return; 
  3361.  
  3362. // Update the lock, as by this point we've definitely got a lock, just need to fire the actions. 
  3363. update_option( $lock_name, time() ); 
  3364.  
  3365. // Get a list of shared terms (those with more than one associated row in term_taxonomy). 
  3366. $shared_terms = $wpdb->get_results( 
  3367. "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt 
  3368. LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id 
  3369. GROUP BY t.term_id 
  3370. HAVING term_tt_count > 1 
  3371. LIMIT 10" 
  3372. ); 
  3373.  
  3374. // No more terms, we're done here. 
  3375. if ( ! $shared_terms ) { 
  3376. update_option( 'finished_splitting_shared_terms', true ); 
  3377. delete_option( $lock_name ); 
  3378. return; 
  3379.  
  3380. // Shared terms found? We'll need to run this script again. 
  3381. wp_schedule_single_event( time() + ( 2 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' ); 
  3382.  
  3383. // Rekey shared term array for faster lookups. 
  3384. $_shared_terms = array(); 
  3385. foreach ( $shared_terms as $shared_term ) { 
  3386. $term_id = intval( $shared_term->term_id ); 
  3387. $_shared_terms[ $term_id ] = $shared_term; 
  3388. $shared_terms = $_shared_terms; 
  3389.  
  3390. // Get term taxonomy data for all shared terms. 
  3391. $shared_term_ids = implode( ', ', array_keys( $shared_terms ) ); 
  3392. $shared_tts = $wpdb->get_results( "SELECT * FROM {$wpdb->term_taxonomy} WHERE `term_id` IN ({$shared_term_ids})" ); 
  3393.  
  3394. // Split term data recording is slow, so we do it just once, outside the loop. 
  3395. $split_term_data = get_option( '_split_terms', array() ); 
  3396. $skipped_first_term = $taxonomies = array(); 
  3397. foreach ( $shared_tts as $shared_tt ) { 
  3398. $term_id = intval( $shared_tt->term_id ); 
  3399.  
  3400. // Don't split the first tt belonging to a given term_id. 
  3401. if ( ! isset( $skipped_first_term[ $term_id ] ) ) { 
  3402. $skipped_first_term[ $term_id ] = 1; 
  3403. continue; 
  3404.  
  3405. if ( ! isset( $split_term_data[ $term_id ] ) ) { 
  3406. $split_term_data[ $term_id ] = array(); 
  3407.  
  3408. // Keep track of taxonomies whose hierarchies need flushing. 
  3409. if ( ! isset( $taxonomies[ $shared_tt->taxonomy ] ) ) { 
  3410. $taxonomies[ $shared_tt->taxonomy ] = 1; 
  3411.  
  3412. // Split the term. 
  3413. $split_term_data[ $term_id ][ $shared_tt->taxonomy ] = _split_shared_term( $shared_terms[ $term_id ], $shared_tt, false ); 
  3414.  
  3415. // Rebuild the cached hierarchy for each affected taxonomy. 
  3416. foreach ( array_keys( $taxonomies ) as $tax ) { 
  3417. delete_option( "{$tax}_children" ); 
  3418. _get_term_hierarchy( $tax ); 
  3419.  
  3420. update_option( '_split_terms', $split_term_data ); 
  3421.  
  3422. delete_option( $lock_name ); 
  3423.  
  3424. /** 
  3425. * In order to avoid the _wp_batch_split_terms() job being accidentally removed,  
  3426. * check that it's still scheduled while we haven't finished splitting terms. 
  3427. * 
  3428. * @ignore 
  3429. * @since 4.3.0 
  3430. */ 
  3431. function _wp_check_for_scheduled_split_terms() { 
  3432. if ( ! get_option( 'finished_splitting_shared_terms' ) && ! wp_next_scheduled( 'wp_split_shared_term_batch' ) ) { 
  3433. wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'wp_split_shared_term_batch' ); 
  3434.  
  3435. /** 
  3436. * Check default categories when a term gets split to see if any of them need to be updated. 
  3437. * 
  3438. * @ignore 
  3439. * @since 4.2.0 
  3440. * 
  3441. * @param int $term_id ID of the formerly shared term. 
  3442. * @param int $new_term_id ID of the new term created for the $term_taxonomy_id. 
  3443. * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split. 
  3444. * @param string $taxonomy Taxonomy for the split term. 
  3445. */ 
  3446. function _wp_check_split_default_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) { 
  3447. if ( 'category' != $taxonomy ) { 
  3448. return; 
  3449.  
  3450. foreach ( array( 'default_category', 'default_link_category', 'default_email_category' ) as $option ) { 
  3451. if ( $term_id == get_option( $option, -1 ) ) { 
  3452. update_option( $option, $new_term_id ); 
  3453.  
  3454. /** 
  3455. * Check menu items when a term gets split to see if any of them need to be updated. 
  3456. * 
  3457. * @ignore 
  3458. * @since 4.2.0 
  3459. * 
  3460. * @global wpdb $wpdb WordPress database abstraction object. 
  3461. * 
  3462. * @param int $term_id ID of the formerly shared term. 
  3463. * @param int $new_term_id ID of the new term created for the $term_taxonomy_id. 
  3464. * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split. 
  3465. * @param string $taxonomy Taxonomy for the split term. 
  3466. */ 
  3467. function _wp_check_split_terms_in_menus( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) { 
  3468. global $wpdb; 
  3469. $post_ids = $wpdb->get_col( $wpdb->prepare( 
  3470. "SELECT m1.post_id 
  3471. FROM {$wpdb->postmeta} AS m1 
  3472. INNER JOIN {$wpdb->postmeta} AS m2 ON ( m2.post_id = m1.post_id ) 
  3473. INNER JOIN {$wpdb->postmeta} AS m3 ON ( m3.post_id = m1.post_id ) 
  3474. WHERE ( m1.meta_key = '_menu_item_type' AND m1.meta_value = 'taxonomy' ) 
  3475. AND ( m2.meta_key = '_menu_item_object' AND m2.meta_value = '%s' ) 
  3476. AND ( m3.meta_key = '_menu_item_object_id' AND m3.meta_value = %d )",  
  3477. $taxonomy,  
  3478. $term_id 
  3479. ) ); 
  3480.  
  3481. if ( $post_ids ) { 
  3482. foreach ( $post_ids as $post_id ) { 
  3483. update_post_meta( $post_id, '_menu_item_object_id', $new_term_id, $term_id ); 
  3484.  
  3485. /** 
  3486. * If the term being split is a nav_menu, change associations. 
  3487. * 
  3488. * @ignore 
  3489. * @since 4.3.0 
  3490. * 
  3491. * @param int $term_id ID of the formerly shared term. 
  3492. * @param int $new_term_id ID of the new term created for the $term_taxonomy_id. 
  3493. * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split. 
  3494. * @param string $taxonomy Taxonomy for the split term. 
  3495. */ 
  3496. function _wp_check_split_nav_menu_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) { 
  3497. if ( 'nav_menu' !== $taxonomy ) { 
  3498. return; 
  3499.  
  3500. // Update menu locations. 
  3501. $locations = get_nav_menu_locations(); 
  3502. foreach ( $locations as $location => $menu_id ) { 
  3503. if ( $term_id == $menu_id ) { 
  3504. $locations[ $location ] = $new_term_id; 
  3505. set_theme_mod( 'nav_menu_locations', $locations ); 
  3506.  
  3507. /** 
  3508. * Get data about terms that previously shared a single term_id, but have since been split. 
  3509. * 
  3510. * @since 4.2.0 
  3511. * 
  3512. * @param int $old_term_id Term ID. This is the old, pre-split term ID. 
  3513. * @return array Array of new term IDs, keyed by taxonomy. 
  3514. */ 
  3515. function wp_get_split_terms( $old_term_id ) { 
  3516. $split_terms = get_option( '_split_terms', array() ); 
  3517.  
  3518. $terms = array(); 
  3519. if ( isset( $split_terms[ $old_term_id ] ) ) { 
  3520. $terms = $split_terms[ $old_term_id ]; 
  3521.  
  3522. return $terms; 
  3523.  
  3524. /** 
  3525. * Get the new term ID corresponding to a previously split term. 
  3526. * 
  3527. * @since 4.2.0 
  3528. * 
  3529. * @param int $old_term_id Term ID. This is the old, pre-split term ID. 
  3530. * @param string $taxonomy Taxonomy that the term belongs to. 
  3531. * @return int|false If a previously split term is found corresponding to the old term_id and taxonomy,  
  3532. * the new term_id will be returned. If no previously split term is found matching 
  3533. * the parameters, returns false. 
  3534. */ 
  3535. function wp_get_split_term( $old_term_id, $taxonomy ) { 
  3536. $split_terms = wp_get_split_terms( $old_term_id ); 
  3537.  
  3538. $term_id = false; 
  3539. if ( isset( $split_terms[ $taxonomy ] ) ) { 
  3540. $term_id = (int) $split_terms[ $taxonomy ]; 
  3541.  
  3542. return $term_id; 
  3543.  
  3544. /** 
  3545. * Determine whether a term is shared between multiple taxonomies. 
  3546. * 
  3547. * Shared taxonomy terms began to be split in 4.3, but failed cron tasks or other delays in upgrade routines may cause 
  3548. * shared terms to remain. 
  3549. * 
  3550. * @since 4.4.0 
  3551. * 
  3552. * @param int $term_id 
  3553. * @return bool 
  3554. */ 
  3555. function wp_term_is_shared( $term_id ) { 
  3556. global $wpdb; 
  3557.  
  3558. if ( get_option( 'finished_splitting_shared_terms' ) ) { 
  3559. return false; 
  3560.  
  3561. $tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); 
  3562.  
  3563. return $tt_count > 1; 
  3564.  
  3565. /** 
  3566. * Generate a permalink for a taxonomy term archive. 
  3567. * 
  3568. * @since 2.5.0 
  3569. * 
  3570. * @global WP_Rewrite $wp_rewrite 
  3571. * 
  3572. * @param object|int|string $term The term object, ID, or slug whose link will be retrieved. 
  3573. * @param string $taxonomy Optional. Taxonomy. Default empty. 
  3574. * @return string|WP_Error HTML link to taxonomy term archive on success, WP_Error if term does not exist. 
  3575. */ 
  3576. function get_term_link( $term, $taxonomy = '' ) { 
  3577. global $wp_rewrite; 
  3578.  
  3579. if ( !is_object($term) ) { 
  3580. if ( is_int( $term ) ) { 
  3581. $term = get_term( $term, $taxonomy ); 
  3582. } else { 
  3583. $term = get_term_by( 'slug', $term, $taxonomy ); 
  3584.  
  3585. if ( !is_object($term) ) 
  3586. $term = new WP_Error('invalid_term', __('Empty Term')); 
  3587.  
  3588. if ( is_wp_error( $term ) ) 
  3589. return $term; 
  3590.  
  3591. $taxonomy = $term->taxonomy; 
  3592.  
  3593. $termlink = $wp_rewrite->get_extra_permastruct($taxonomy); 
  3594.  
  3595. $slug = $term->slug; 
  3596. $t = get_taxonomy($taxonomy); 
  3597.  
  3598. if ( empty($termlink) ) { 
  3599. if ( 'category' == $taxonomy ) 
  3600. $termlink = '?cat=' . $term->term_id; 
  3601. elseif ( $t->query_var ) 
  3602. $termlink = "?$t->query_var=$slug"; 
  3603. else 
  3604. $termlink = "?taxonomy=$taxonomy&term=$slug"; 
  3605. $termlink = home_url($termlink); 
  3606. } else { 
  3607. if ( $t->rewrite['hierarchical'] ) { 
  3608. $hierarchical_slugs = array(); 
  3609. $ancestors = get_ancestors( $term->term_id, $taxonomy, 'taxonomy' ); 
  3610. foreach ( (array)$ancestors as $ancestor ) { 
  3611. $ancestor_term = get_term($ancestor, $taxonomy); 
  3612. $hierarchical_slugs[] = $ancestor_term->slug; 
  3613. $hierarchical_slugs = array_reverse($hierarchical_slugs); 
  3614. $hierarchical_slugs[] = $slug; 
  3615. $termlink = str_replace("%$taxonomy%", implode('/', $hierarchical_slugs), $termlink); 
  3616. } else { 
  3617. $termlink = str_replace("%$taxonomy%", $slug, $termlink); 
  3618. $termlink = home_url( user_trailingslashit($termlink, 'category') ); 
  3619. // Back Compat filters. 
  3620. if ( 'post_tag' == $taxonomy ) { 
  3621.  
  3622. /** 
  3623. * Filters the tag link. 
  3624. * 
  3625. * @since 2.3.0 
  3626. * @deprecated 2.5.0 Use 'term_link' instead. 
  3627. * 
  3628. * @param string $termlink Tag link URL. 
  3629. * @param int $term_id Term ID. 
  3630. */ 
  3631. $termlink = apply_filters( 'tag_link', $termlink, $term->term_id ); 
  3632. } elseif ( 'category' == $taxonomy ) { 
  3633.  
  3634. /** 
  3635. * Filters the category link. 
  3636. * 
  3637. * @since 1.5.0 
  3638. * @deprecated 2.5.0 Use 'term_link' instead. 
  3639. * 
  3640. * @param string $termlink Category link URL. 
  3641. * @param int $term_id Term ID. 
  3642. */ 
  3643. $termlink = apply_filters( 'category_link', $termlink, $term->term_id ); 
  3644.  
  3645. /** 
  3646. * Filters the term link. 
  3647. * 
  3648. * @since 2.5.0 
  3649. * 
  3650. * @param string $termlink Term link URL. 
  3651. * @param object $term Term object. 
  3652. * @param string $taxonomy Taxonomy slug. 
  3653. */ 
  3654. return apply_filters( 'term_link', $termlink, $term, $taxonomy ); 
  3655.  
  3656. /** 
  3657. * Display the taxonomies of a post with available options. 
  3658. * 
  3659. * This function can be used within the loop to display the taxonomies for a 
  3660. * post without specifying the Post ID. You can also use it outside the Loop to 
  3661. * display the taxonomies for a specific post. 
  3662. * 
  3663. * @since 2.5.0 
  3664. * 
  3665. * @param array $args { 
  3666. * Arguments about which post to use and how to format the output. Shares all of the arguments 
  3667. * supported by get_the_taxonomies(), in addition to the following. 
  3668. * 
  3669. * @type int|WP_Post $post Post ID or object to get taxonomies of. Default current post. 
  3670. * @type string $before Displays before the taxonomies. Default empty string. 
  3671. * @type string $sep Separates each taxonomy. Default is a space. 
  3672. * @type string $after Displays after the taxonomies. Default empty string. 
  3673. * } 
  3674. */ 
  3675. function the_taxonomies( $args = array() ) { 
  3676. $defaults = array( 
  3677. 'post' => 0,  
  3678. 'before' => '',  
  3679. 'sep' => ' ',  
  3680. 'after' => '',  
  3681. ); 
  3682.  
  3683. $r = wp_parse_args( $args, $defaults ); 
  3684.  
  3685. echo $r['before'] . join( $r['sep'], get_the_taxonomies( $r['post'], $r ) ) . $r['after']; 
  3686.  
  3687. /** 
  3688. * Retrieve all taxonomies associated with a post. 
  3689. * 
  3690. * This function can be used within the loop. It will also return an array of 
  3691. * the taxonomies with links to the taxonomy and name. 
  3692. * 
  3693. * @since 2.5.0 
  3694. * 
  3695. * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 
  3696. * @param array $args { 
  3697. * Optional. Arguments about how to format the list of taxonomies. Default empty array. 
  3698. * 
  3699. * @type string $template Template for displaying a taxonomy label and list of terms. 
  3700. * Default is "Label: Terms." 
  3701. * @type string $term_template Template for displaying a single term in the list. Default is the term name 
  3702. * linked to its archive. 
  3703. * } 
  3704. * @return array List of taxonomies. 
  3705. */ 
  3706. function get_the_taxonomies( $post = 0, $args = array() ) { 
  3707. $post = get_post( $post ); 
  3708.  
  3709. $args = wp_parse_args( $args, array( 
  3710. /** translators: %s: taxonomy label, %l: list of terms formatted as per $term_template */ 
  3711. 'template' => __( '%s: %l.' ),  
  3712. 'term_template' => '<a href="%1$s">%2$s</a>',  
  3713. ) ); 
  3714.  
  3715. $taxonomies = array(); 
  3716.  
  3717. if ( ! $post ) { 
  3718. return $taxonomies; 
  3719.  
  3720. foreach ( get_object_taxonomies( $post ) as $taxonomy ) { 
  3721. $t = (array) get_taxonomy( $taxonomy ); 
  3722. if ( empty( $t['label'] ) ) { 
  3723. $t['label'] = $taxonomy; 
  3724. if ( empty( $t['args'] ) ) { 
  3725. $t['args'] = array(); 
  3726. if ( empty( $t['template'] ) ) { 
  3727. $t['template'] = $args['template']; 
  3728. if ( empty( $t['term_template'] ) ) { 
  3729. $t['term_template'] = $args['term_template']; 
  3730.  
  3731. $terms = get_object_term_cache( $post->ID, $taxonomy ); 
  3732. if ( false === $terms ) { 
  3733. $terms = wp_get_object_terms( $post->ID, $taxonomy, $t['args'] ); 
  3734. $links = array(); 
  3735.  
  3736. foreach ( $terms as $term ) { 
  3737. $links[] = wp_sprintf( $t['term_template'], esc_attr( get_term_link( $term ) ), $term->name ); 
  3738. if ( $links ) { 
  3739. $taxonomies[$taxonomy] = wp_sprintf( $t['template'], $t['label'], $links, $terms ); 
  3740. return $taxonomies; 
  3741.  
  3742. /** 
  3743. * Retrieve all taxonomies of a post with just the names. 
  3744. * 
  3745. * @since 2.5.0 
  3746. * 
  3747. * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 
  3748. * @return array 
  3749. */ 
  3750. function get_post_taxonomies( $post = 0 ) { 
  3751. $post = get_post( $post ); 
  3752.  
  3753. return get_object_taxonomies($post); 
  3754.  
  3755. /** 
  3756. * Determine if the given object is associated with any of the given terms. 
  3757. * 
  3758. * The given terms are checked against the object's terms' term_ids, names and slugs. 
  3759. * Terms given as integers will only be checked against the object's terms' term_ids. 
  3760. * If no terms are given, determines if object is associated with any terms in the given taxonomy. 
  3761. * 
  3762. * @since 2.7.0 
  3763. * 
  3764. * @param int $object_id ID of the object (post ID, link ID, ...). 
  3765. * @param string $taxonomy Single taxonomy name. 
  3766. * @param int|string|array $terms Optional. Term term_id, name, slug or array of said. Default null. 
  3767. * @return bool|WP_Error WP_Error on input error. 
  3768. */ 
  3769. function is_object_in_term( $object_id, $taxonomy, $terms = null ) { 
  3770. if ( !$object_id = (int) $object_id ) 
  3771. return new WP_Error( 'invalid_object', __( 'Invalid object ID' ) ); 
  3772.  
  3773. $object_terms = get_object_term_cache( $object_id, $taxonomy ); 
  3774. if ( false === $object_terms ) { 
  3775. $object_terms = wp_get_object_terms( $object_id, $taxonomy, array( 'update_term_meta_cache' => false ) ); 
  3776. if ( is_wp_error( $object_terms ) ) { 
  3777. return $object_terms; 
  3778.  
  3779. wp_cache_set( $object_id, wp_list_pluck( $object_terms, 'term_id' ), "{$taxonomy}_relationships" ); 
  3780.  
  3781. if ( is_wp_error( $object_terms ) ) 
  3782. return $object_terms; 
  3783. if ( empty( $object_terms ) ) 
  3784. return false; 
  3785. if ( empty( $terms ) ) 
  3786. return ( !empty( $object_terms ) ); 
  3787.  
  3788. $terms = (array) $terms; 
  3789.  
  3790. if ( $ints = array_filter( $terms, 'is_int' ) ) 
  3791. $strs = array_diff( $terms, $ints ); 
  3792. else 
  3793. $strs =& $terms; 
  3794.  
  3795. foreach ( $object_terms as $object_term ) { 
  3796. // If term is an int, check against term_ids only. 
  3797. if ( $ints && in_array( $object_term->term_id, $ints ) ) { 
  3798. return true; 
  3799.  
  3800. if ( $strs ) { 
  3801. // Only check numeric strings against term_id, to avoid false matches due to type juggling. 
  3802. $numeric_strs = array_map( 'intval', array_filter( $strs, 'is_numeric' ) ); 
  3803. if ( in_array( $object_term->term_id, $numeric_strs, true ) ) { 
  3804. return true; 
  3805.  
  3806. if ( in_array( $object_term->name, $strs ) ) return true; 
  3807. if ( in_array( $object_term->slug, $strs ) ) return true; 
  3808.  
  3809. return false; 
  3810.  
  3811. /** 
  3812. * Determine if the given object type is associated with the given taxonomy. 
  3813. * 
  3814. * @since 3.0.0 
  3815. * 
  3816. * @param string $object_type Object type string. 
  3817. * @param string $taxonomy Single taxonomy name. 
  3818. * @return bool True if object is associated with the taxonomy, otherwise false. 
  3819. */ 
  3820. function is_object_in_taxonomy( $object_type, $taxonomy ) { 
  3821. $taxonomies = get_object_taxonomies( $object_type ); 
  3822. if ( empty( $taxonomies ) ) { 
  3823. return false; 
  3824. return in_array( $taxonomy, $taxonomies ); 
  3825.  
  3826. /** 
  3827. * Get an array of ancestor IDs for a given object. 
  3828. * 
  3829. * @since 3.1.0 
  3830. * @since 4.1.0 Introduced the `$resource_type` argument. 
  3831. * 
  3832. * @param int $object_id Optional. The ID of the object. Default 0. 
  3833. * @param string $object_type Optional. The type of object for which we'll be retrieving 
  3834. * ancestors. Accepts a post type or a taxonomy name. Default empty. 
  3835. * @param string $resource_type Optional. Type of resource $object_type is. Accepts 'post_type' 
  3836. * or 'taxonomy'. Default empty. 
  3837. * @return array An array of ancestors from lowest to highest in the hierarchy. 
  3838. */ 
  3839. function get_ancestors( $object_id = 0, $object_type = '', $resource_type = '' ) { 
  3840. $object_id = (int) $object_id; 
  3841.  
  3842. $ancestors = array(); 
  3843.  
  3844. if ( empty( $object_id ) ) { 
  3845.  
  3846. /** This filter is documented in wp-includes/taxonomy.php */ 
  3847. return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type ); 
  3848.  
  3849. if ( ! $resource_type ) { 
  3850. if ( is_taxonomy_hierarchical( $object_type ) ) { 
  3851. $resource_type = 'taxonomy'; 
  3852. } elseif ( post_type_exists( $object_type ) ) { 
  3853. $resource_type = 'post_type'; 
  3854.  
  3855. if ( 'taxonomy' === $resource_type ) { 
  3856. $term = get_term($object_id, $object_type); 
  3857. while ( ! is_wp_error($term) && ! empty( $term->parent ) && ! in_array( $term->parent, $ancestors ) ) { 
  3858. $ancestors[] = (int) $term->parent; 
  3859. $term = get_term($term->parent, $object_type); 
  3860. } elseif ( 'post_type' === $resource_type ) { 
  3861. $ancestors = get_post_ancestors($object_id); 
  3862.  
  3863. /** 
  3864. * Filters a given object's ancestors. 
  3865. * 
  3866. * @since 3.1.0 
  3867. * @since 4.1.1 Introduced the `$resource_type` parameter. 
  3868. * 
  3869. * @param array $ancestors An array of object ancestors. 
  3870. * @param int $object_id Object ID. 
  3871. * @param string $object_type Type of object. 
  3872. * @param string $resource_type Type of resource $object_type is. 
  3873. */ 
  3874. return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type ); 
  3875.  
  3876. /** 
  3877. * Returns the term's parent's term_ID. 
  3878. * 
  3879. * @since 3.1.0 
  3880. * 
  3881. * @param int $term_id Term ID. 
  3882. * @param string $taxonomy Taxonomy name. 
  3883. * @return int|false False on error. 
  3884. */ 
  3885. function wp_get_term_taxonomy_parent_id( $term_id, $taxonomy ) { 
  3886. $term = get_term( $term_id, $taxonomy ); 
  3887. if ( ! $term || is_wp_error( $term ) ) { 
  3888. return false; 
  3889. return (int) $term->parent; 
  3890.  
  3891. /** 
  3892. * Checks the given subset of the term hierarchy for hierarchy loops. 
  3893. * Prevents loops from forming and breaks those that it finds. 
  3894. * 
  3895. * Attached to the {@see 'wp_update_term_parent'} filter. 
  3896. * 
  3897. * @since 3.1.0 
  3898. * 
  3899. * @param int $parent `term_id` of the parent for the term we're checking. 
  3900. * @param int $term_id The term we're checking. 
  3901. * @param string $taxonomy The taxonomy of the term we're checking. 
  3902. * 
  3903. * @return int The new parent for the term. 
  3904. */ 
  3905. function wp_check_term_hierarchy_for_loops( $parent, $term_id, $taxonomy ) { 
  3906. // Nothing fancy here - bail 
  3907. if ( !$parent ) 
  3908. return 0; 
  3909.  
  3910. // Can't be its own parent. 
  3911. if ( $parent == $term_id ) 
  3912. return 0; 
  3913.  
  3914. // Now look for larger loops. 
  3915. if ( !$loop = wp_find_hierarchy_loop( 'wp_get_term_taxonomy_parent_id', $term_id, $parent, array( $taxonomy ) ) ) 
  3916. return $parent; // No loop 
  3917.  
  3918. // Setting $parent to the given value causes a loop. 
  3919. if ( isset( $loop[$term_id] ) ) 
  3920. return 0; 
  3921.  
  3922. // There's a loop, but it doesn't contain $term_id. Break the loop. 
  3923. foreach ( array_keys( $loop ) as $loop_member ) 
  3924. wp_update_term( $loop_member, $taxonomy, array( 'parent' => 0 ) ); 
  3925.  
  3926. return $parent; 
.