/bp-forums/bbpress/bb-includes/backpress/class.wp-taxonomy.php

  1. <?php 
  2. // Last sync [WP11537] - Refactored into a class based on wp-includes/taxonomy.php 
  3.  
  4. /** 
  5. * Taxonomy API 
  6. * 
  7. * @package WordPress 
  8. * @subpackage Taxonomy 
  9. * @since 2.3.0 
  10. */ 
  11.  
  12. /** 
  13. * WordPress Taxonomy based off of WordPress revision 8782. 
  14. * 
  15. * @since 2.3.0 
  16. */ 
  17. class WP_Taxonomy { 
  18. /** 
  19. * Stores the database. 
  20. * 
  21. * @var unknown_type 
  22. */ 
  23. var $db; 
  24. var $taxonomies = array(); 
  25.  
  26. function WP_Taxonomy( &$db ) { 
  27. $this->__construct( $db ); 
  28. register_shutdown_function( array(&$this, '__destruct') ); 
  29.  
  30. /** 
  31. * PHP5 constructor - Assigns the database to an attribute of the class. 
  32. * 
  33. * @param unknown_type $db 
  34. */ 
  35. function __construct( &$db ) { 
  36. $this->db =& $db; 
  37.  
  38. /** 
  39. * Does nothing. 
  40. * 
  41. * @package BackPress 
  42. * @subpackage Taxonomy 
  43. */ 
  44. function __destruct() { 
  45.  
  46. /** 
  47. * Return all of the taxonomy names that are of $object_type. 
  48. * 
  49. * It appears that this function can be used to find all of the names inside of 
  50. * $this->taxonomies variable. 
  51. * 
  52. * <code><?php $taxonomies = $this->get_object_taxonomies('post'); ?></code> Should 
  53. * result in <code>Array('category', 'post_tag')</code> 
  54. * 
  55. * @package WordPress 
  56. * @subpackage Taxonomy 
  57. * @since 2.3.0 
  58. * 
  59. * @uses $this->taxonomies 
  60. * 
  61. * @param array|string|object $object_type Name of the type of taxonomy object, or an object (row from posts) 
  62. * @return array The names of all taxonomy of $object_type. 
  63. */ 
  64. function get_object_taxonomies($object_type) { 
  65. $object_type = (array) $object_type; 
  66.  
  67. // WP DIFF 
  68. $taxonomies = array(); 
  69. foreach ( (array) $this->taxonomies as $taxonomy ) { 
  70. if ( array_intersect($object_type, (array) $taxonomy->object_type) ) 
  71. $taxonomies[] = $taxonomy->name; 
  72.  
  73. return $taxonomies; 
  74.  
  75. /** 
  76. * Retrieves the taxonomy object of $taxonomy. 
  77. * 
  78. * The get_taxonomy function will first check that the parameter string given 
  79. * is a taxonomy object and if it is, it will return it. 
  80. * 
  81. * @package WordPress 
  82. * @subpackage Taxonomy 
  83. * @since 2.3.0 
  84. * 
  85. * @uses $this->taxonomies 
  86. * @uses $this->is_taxonomy() Checks whether taxonomy exists 
  87. * 
  88. * @param string $taxonomy Name of taxonomy object to return 
  89. * @return object|bool The Taxonomy Object or false if $taxonomy doesn't exist 
  90. */ 
  91. function get_taxonomy( $taxonomy ) { 
  92. if ( !$this->is_taxonomy($taxonomy) ) 
  93. return false; 
  94.  
  95. return $this->taxonomies[$taxonomy]; 
  96.  
  97. /** 
  98. * Checks that the taxonomy name exists. 
  99. * 
  100. * @package WordPress 
  101. * @subpackage Taxonomy 
  102. * @since 2.3.0 
  103. *  
  104. * @uses $this->taxonomies 
  105. * 
  106. * @param string $taxonomy Name of taxonomy object 
  107. * @return bool Whether the taxonomy exists or not. 
  108. */ 
  109. function is_taxonomy( $taxonomy ) { 
  110. return isset($this->taxonomies[$taxonomy]); 
  111.  
  112. /** 
  113. * Whether the taxonomy object is hierarchical. 
  114. * 
  115. * Checks to make sure that the taxonomy is an object first. Then Gets the 
  116. * object, and finally returns the hierarchical value in the object. 
  117. * 
  118. * A false return value might also mean that the taxonomy does not exist. 
  119. * 
  120. * @package WordPress 
  121. * @subpackage Taxonomy 
  122. * @since 2.3.0 
  123. * 
  124. * @uses $this->is_taxonomy() Checks whether taxonomy exists 
  125. * @uses $this->get_taxonomy() Used to get the taxonomy object 
  126. * 
  127. * @param string $taxonomy Name of taxonomy object 
  128. * @return bool Whether the taxonomy is hierarchical 
  129. */ 
  130. function is_taxonomy_hierarchical($taxonomy) { 
  131. if ( !$this->is_taxonomy($taxonomy) ) 
  132. return false; 
  133.  
  134. $taxonomy = $this->get_taxonomy($taxonomy); 
  135. return $taxonomy->hierarchical; 
  136.  
  137. /** 
  138. * Create or modify a taxonomy object. Do not use before init. 
  139. * 
  140. * A simple function for creating or modifying a taxonomy object based on the 
  141. * parameters given. The function will accept an array (third optional 
  142. * parameter), along with strings for the taxonomy name and another string for 
  143. * the object type. 
  144. * 
  145. * The function keeps a default set, allowing for the $args to be optional but 
  146. * allow the other functions to still work. It is possible to overwrite the 
  147. * default set, which contains two keys: hierarchical and update_count_callback. 
  148. * 
  149. * Nothing is returned, so expect error maybe or use is_taxonomy() to check 
  150. * whether taxonomy exists. 
  151. * 
  152. * Optional $args contents: 
  153. * 
  154. * hierarachical - has some defined purpose at other parts of the API and is a 
  155. * boolean value. 
  156. * 
  157. * update_count_callback - works much like a hook, in that it will be called 
  158. * when the count is updated. 
  159. * 
  160. * @package WordPress 
  161. * @subpackage Taxonomy 
  162. * @since 2.3.0 
  163. * @uses $this->taxonomies Inserts new taxonomy object into the list 
  164. *  
  165. * @param string $taxonomy Name of taxonomy object 
  166. * @param string $object_type Name of the object type for the taxonomy object. 
  167. * @param array|string $args See above description for the two keys values. 
  168. */ 
  169. function register_taxonomy( $taxonomy, $object_type, $args = array() ) { 
  170. $defaults = array('hierarchical' => false, 'update_count_callback' => ''); 
  171. $args = wp_parse_args($args, $defaults); 
  172.  
  173. $args['name'] = $taxonomy; 
  174. $args['object_type'] = $object_type; 
  175. $this->taxonomies[$taxonomy] = (object) $args; 
  176.  
  177. // 
  178. // Term API 
  179. // 
  180.   
  181. /** 
  182. * Retrieve object_ids of valid taxonomy and term. 
  183. * 
  184. * The strings of $taxonomies must exist before this function will continue. On 
  185. * failure of finding a valid taxonomy, it will return an WP_Error class, kind 
  186. * of like Exceptions in PHP 5, except you can't catch them. Even so, you can 
  187. * still test for the WP_Error class and get the error message. 
  188. * 
  189. * The $terms aren't checked the same as $taxonomies, but still need to exist 
  190. * for $object_ids to be returned. 
  191. * 
  192. * It is possible to change the order that object_ids is returned by either 
  193. * using PHP sort family functions or using the database by using $args with 
  194. * either ASC or DESC array. The value should be in the key named 'order'. 
  195. * 
  196. * @package WordPress 
  197. * @subpackage Taxonomy 
  198. * @since 2.3.0 
  199. * 
  200. * @uses wp_parse_args() Creates an array from string $args. 
  201. * 
  202. * @param string|array $terms String of term or array of string values of terms that will be used 
  203. * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names 
  204. * @param array|string $args Change the order of the object_ids, either ASC or DESC 
  205. * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success 
  206. * the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found. 
  207. */ 
  208. function get_objects_in_term( $terms, $taxonomies, $args = null ) { 
  209. if ( !is_array($terms) ) 
  210. $terms = array($terms); 
  211.  
  212. if ( !is_array($taxonomies) ) 
  213. $taxonomies = array($taxonomies); 
  214.  
  215. foreach ( (array) $taxonomies as $taxonomy ) { 
  216. if ( !$this->is_taxonomy($taxonomy) ) 
  217. return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 
  218.  
  219. $defaults = array('order' => 'ASC', 'field' => 'term_id'); 
  220. $args = wp_parse_args( $args, $defaults ); 
  221. extract($args, EXTR_SKIP); 
  222.  
  223. if ( 'tt_id' == $field ) 
  224. $field = 'tt.term_taxonomy_id'; 
  225. else 
  226. $field = 'tt.term_id'; 
  227.  
  228. $order = ( 'desc' == strtolower($order) ) ? 'DESC' : 'ASC'; 
  229.  
  230. $terms = array_map('intval', $terms); 
  231.  
  232. $taxonomies = "'" . implode("', '", $taxonomies) . "'"; 
  233. $terms = "'" . implode("', '", $terms) . "'"; 
  234.  
  235. $object_ids = $this->db->get_col("SELECT tr.object_id FROM {$this->db->term_relationships} AS tr INNER JOIN {$this->db->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND $field IN ($terms) ORDER BY tr.object_id $order"); 
  236.  
  237. if ( ! $object_ids ) 
  238. return array(); 
  239.  
  240. return $object_ids; 
  241.  
  242. /** 
  243. * Get all Term data from database by Term ID. 
  244. * 
  245. * The usage of the get_term function is to apply filters to a term object. It 
  246. * is possible to get a term object from the database before applying the 
  247. * filters. 
  248. * 
  249. * $term ID must be part of $taxonomy, to get from the database. Failure, might 
  250. * be able to be captured by the hooks. Failure would be the same value as $this->db 
  251. * returns for the get_row method. 
  252. * 
  253. * There are two hooks, one is specifically for each term, named 'get_term', and 
  254. * the second is for the taxonomy name, 'term_$taxonomy'. Both hooks gets the 
  255. * term object, and the taxonomy name as parameters. Both hooks are expected to 
  256. * return a Term object. 
  257. * 
  258. * 'get_term' hook - Takes two parameters the term Object and the taxonomy name. 
  259. * Must return term object. Used in get_term() as a catch-all filter for every 
  260. * $term. 
  261. * 
  262. * 'get_$taxonomy' hook - Takes two parameters the term Object and the taxonomy 
  263. * name. Must return term object. $taxonomy will be the taxonomy name, so for 
  264. * example, if 'category', it would be 'get_category' as the filter name. Useful 
  265. * for custom taxonomies or plugging into default taxonomies. 
  266. * 
  267. * @package WordPress 
  268. * @subpackage Taxonomy 
  269. * @since 2.3.0 
  270. * 
  271. * @uses $this->sanitize_term() Cleanses the term based on $filter context before returning. 
  272. * @see $this->sanitize_term_field() The $context param lists the available values for get_term_by() $filter param. 
  273. * 
  274. * @param int|object $term If integer, will get from database. If object will apply filters and return $term. 
  275. * @param string $taxonomy Taxonomy name that $term is part of. 
  276. * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N 
  277. * @param string $filter Optional, default is raw or no WordPress defined filter will applied. 
  278. * @return mixed|null|WP_Error Term Row from database. Will return null if $term is empty. If taxonomy does not 
  279. * exist then WP_Error will be returned. 
  280. */ 
  281. function &get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') { 
  282. if ( empty($term) ) { 
  283. $error = new WP_Error('invalid_term', __('Empty Term')); 
  284. return $error; 
  285.  
  286. if ( !$this->is_taxonomy($taxonomy) ) { 
  287. $error = new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 
  288. return $error; 
  289.  
  290. if ( is_object($term) ) { 
  291. wp_cache_add($term->term_id, $term, $taxonomy); 
  292. wp_cache_add($term->term_taxonomy_id, $term->term_id, "$taxonomy:tt_id" ); 
  293. $_term = $term; 
  294. } else { 
  295. $term = (int) $term; 
  296. if ( ! $_term = wp_cache_get($term, $taxonomy) ) { 
  297. $_term = $this->db->get_row( $this->db->prepare( "SELECT t.*, tt.* FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND t.term_id = %s LIMIT 1", $taxonomy, $term) ); 
  298. wp_cache_add($term, $_term, $taxonomy); 
  299. wp_cache_add($_term->term_taxonomy_id, $_term->term_id, "$taxonomy:tt_id" ); 
  300.  
  301. $_term = apply_filters('get_term', $_term, $taxonomy); 
  302. $_term = apply_filters("get_$taxonomy", $_term, $taxonomy); 
  303. $_term = $this->sanitize_term($_term, $taxonomy, $filter); 
  304.  
  305. backpress_convert_object( $_term, $output ); 
  306.  
  307. return $_term; 
  308.  
  309. /** 
  310. * Get all Term data from database by Term field and data. 
  311. * 
  312. * Warning: $value is not escaped for 'name' $field. You must do it yourself, if 
  313. * required. 
  314. * 
  315. * The default $field is 'id', therefore it is possible to also use null for 
  316. * field, but not recommended that you do so. 
  317. * 
  318. * If $value does not exist, the return value will be false. If $taxonomy exists 
  319. * and $field and $value combinations exist, the Term will be returned. 
  320. * 
  321. * @package WordPress 
  322. * @subpackage Taxonomy 
  323. * @since 2.3.0 
  324. * 
  325. * @uses $this->sanitize_term() Cleanses the term based on $filter context before returning. 
  326. * @see $this->sanitize_term_field() The $context param lists the available values for get_term_by() $filter param. 
  327. * 
  328. * @param string $field Either 'slug', 'name', 'id', or 'tt_id' 
  329. * @param string|int $value Search for this term value 
  330. * @param string $taxonomy Taxonomy Name 
  331. * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N 
  332. * @param string $filter Optional, default is raw or no WordPress defined filter will applied. 
  333. * @return mixed Term Row from database. Will return false if $taxonomy does not exist or $term was not found. 
  334. */ 
  335. function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = 'raw') { 
  336. if ( !$this->is_taxonomy($taxonomy) ) 
  337. return false; 
  338.  
  339. if ( 'slug' == $field ) { 
  340. $field = 't.slug'; 
  341. $value = $this->sanitize_term_slug($value, $taxonomy); 
  342. if ( empty($value) ) 
  343. return false; 
  344. } else if ( 'name' == $field ) { 
  345. // Assume already escaped 
  346. $field = 't.name'; 
  347. } else if ( 'tt_id' == $field ) { 
  348. $field = 'tt.term_taxonomy_id'; 
  349. $value = (int) $value; 
  350. if ( $_term_id = wp_cache_get( $value, "$taxonomy:tt_id" ) ) 
  351. return $this->get_term( $_term_id, $taxonomy, $output, $filter ); 
  352. } else { 
  353. $field = 't.term_id'; 
  354. $value = (int) $value; 
  355.  
  356. $term = $this->db->get_row( $this->db->prepare( "SELECT t.*, tt.* FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND $field = %s LIMIT 1", $taxonomy, $value) ); 
  357. if ( !$term ) 
  358. return false; 
  359.  
  360. wp_cache_add($term->term_id, $term, $taxonomy); 
  361. wp_cache_add($term->term_taxonomy_id, $term->term_id, "$taxonomy:tt_id" ); 
  362.  
  363. $term = $this->sanitize_term($term, $taxonomy, $filter); 
  364.  
  365. backpress_convert_object( $term, $output ); 
  366.  
  367. return $term; 
  368.  
  369. /** 
  370. * Merge all term children into a single array of their IDs. 
  371. * 
  372. * This recursive function will merge all of the children of $term into the same 
  373. * array of term IDs. Only useful for taxonomies which are hierarchical. 
  374. * 
  375. * Will return an empty array if $term does not exist in $taxonomy. 
  376. *  
  377. * @package WordPress 
  378. * @subpackage Taxonomy 
  379. * @since 2.3.0 
  380. * 
  381. * @uses $this->_get_term_hierarchy() 
  382. * @uses $this->get_term_children() Used to get the children of both $taxonomy and the parent $term 
  383. * 
  384. * @param string $term ID of Term to get children 
  385. * @param string $taxonomy Taxonomy Name 
  386. * @return array|WP_Error List of Term Objects. WP_Error returned if $taxonomy does not exist 
  387. */ 
  388. function get_term_children( $term_id, $taxonomy ) { 
  389. if ( !$this->is_taxonomy($taxonomy) ) 
  390. return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 
  391.  
  392. $term_id = intval( $term_id ); 
  393.  
  394. $terms = $this->_get_term_hierarchy($taxonomy); 
  395.  
  396. if ( ! isset($terms[$term_id]) ) 
  397. return array(); 
  398.  
  399. $children = $terms[$term_id]; 
  400.  
  401. foreach ( (array) $terms[$term_id] as $child ) { 
  402. if ( isset($terms[$child]) ) 
  403. $children = array_merge($children, $this->get_term_children($child, $taxonomy)); 
  404.  
  405. return $children; 
  406.  
  407. /** 
  408. * Get sanitized Term field. 
  409. * 
  410. * Does checks for $term, based on the $taxonomy. The function is for contextual 
  411. * reasons and for simplicity of usage. See sanitize_term_field() for more 
  412. * information. 
  413. * 
  414. * @package WordPress 
  415. * @subpackage Taxonomy 
  416. * @since 2.3.0 
  417. * 
  418. * @uses $this->sanitize_term_field() Passes the return value in sanitize_term_field on success. 
  419. * 
  420. * @param string $field Term field to fetch 
  421. * @param int $term Term ID 
  422. * @param string $taxonomy Taxonomy Name 
  423. * @param string $context Optional, default is display. Look at sanitize_term_field() for available options. 
  424. * @return mixed Will return an empty string if $term is not an object or if $field is not set in $term. 
  425. */ 
  426. function get_term_field( $field, $term, $taxonomy, $context = 'display' ) { 
  427. $term = (int) $term; 
  428. $term = $this->get_term( $term, $taxonomy ); 
  429. if ( is_wp_error($term) ) 
  430. return $term; 
  431.  
  432. if ( !is_object($term) ) 
  433. return ''; 
  434.  
  435. if ( !isset($term->$field) ) 
  436. return ''; 
  437.  
  438. return $this->sanitize_term_field($field, $term->$field, $term->term_id, $taxonomy, $context); 
  439.  
  440. /** 
  441. * Sanitizes Term for editing. 
  442. * 
  443. * Return value is sanitize_term() and usage is for sanitizing the term for 
  444. * editing. Function is for contextual and simplicity. 
  445. *  
  446. * @package WordPress 
  447. * @subpackage Taxonomy 
  448. * @since 2.3.0 
  449. * 
  450. * @uses $this->sanitize_term() Passes the return value on success 
  451. * 
  452. * @param int|object $id Term ID or Object 
  453. * @param string $taxonomy Taxonomy Name 
  454. * @return mixed|null|WP_Error Will return empty string if $term is not an object. 
  455. */ 
  456. function get_term_to_edit( $id, $taxonomy ) { 
  457. $term = $this->get_term( $id, $taxonomy ); 
  458.  
  459. if ( is_wp_error($term) ) 
  460. return $term; 
  461.  
  462. if ( !is_object($term) ) 
  463. return ''; 
  464.  
  465. return $this->sanitize_term($term, $taxonomy, 'edit'); 
  466.  
  467. /** 
  468. * Retrieve the terms in a given taxonomy or list of taxonomies. 
  469. * 
  470. * You can fully inject any customizations to the query before it is sent, as 
  471. * well as control the output with a filter. 
  472. * 
  473. * The 'get_terms' filter will be called when the cache has the term and will 
  474. * pass the found term along with the array of $taxonomies and array of $args. 
  475. * This filter is also called before the array of terms is passed and will pass 
  476. * the array of terms, along with the $taxonomies and $args. 
  477. * 
  478. * The 'list_terms_exclusions' filter passes the compiled exclusions along with 
  479. * the $args. 
  480. * 
  481. * The 'get_terms_orderby' filter passes the ORDER BY clause for the query 
  482. * along with the $args array. 
  483. * 
  484. * The 'get_terms_fields' filter passes the fields for the SELECT query 
  485. * along with the $args array. 
  486. * 
  487. * The list of arguments that $args can contain, which will overwrite the defaults: 
  488. * 
  489. * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing 
  490. * (will use term_id), Passing a custom value other than these will cause it to 
  491. * order based on the custom value. 
  492. * 
  493. * order - Default is ASC. Can use DESC. 
  494. * 
  495. * hide_empty - Default is true. Will not return empty terms, which means 
  496. * terms whose count is 0 according to the given taxonomy. 
  497. * 
  498. * exclude - Default is an empty string. A comma- or space-delimited string 
  499. * of term ids to exclude from the return array. If 'include' is non-empty,  
  500. * 'exclude' is ignored. 
  501. * 
  502. * include - Default is an empty string. A comma- or space-delimited string 
  503. * of term ids to include in the return array. 
  504. * 
  505. * number - The maximum number of terms to return. Default is empty. 
  506. * 
  507. * offset - The number by which to offset the terms query. 
  508. * 
  509. * fields - Default is 'all', which returns an array of term objects. 
  510. * If 'fields' is 'ids' or 'names', returns an array of 
  511. * integers or strings, respectively. 
  512. * 
  513. * slug - Returns terms whose "slug" matches this value. Default is empty string. 
  514. * 
  515. * hierarchical - Whether to include terms that have non-empty descendants 
  516. * (even if 'hide_empty' is set to true). 
  517. * 
  518. * search - Returned terms' names will contain the value of 'search',  
  519. * case-insensitive. Default is an empty string. 
  520. * 
  521. * name__like - Returned terms' names will begin with the value of 'name__like',  
  522. * case-insensitive. Default is empty string. 
  523. * 
  524. * The argument 'pad_counts', if set to true will include the quantity of a term's 
  525. * children in the quantity of each term's "count" object variable. 
  526. * 
  527. * The 'get' argument, if set to 'all' instead of its default empty string,  
  528. * returns terms regardless of ancestry or whether the terms are empty. 
  529. * 
  530. * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default 
  531. * is 0. If set to a non-zero value, all returned terms will be descendants 
  532. * of that term according to the given taxonomy. Hence 'child_of' is set to 0 
  533. * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies 
  534. * make term ancestry ambiguous. 
  535. * 
  536. * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is 
  537. * the empty string '', which has a different meaning from the integer 0. 
  538. * If set to an integer value, all returned terms will have as an immediate 
  539. * ancestor the term whose ID is specified by that integer according to the given taxonomy. 
  540. * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent' 
  541. * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc. 
  542. * 
  543. * @package WordPress 
  544. * @subpackage Taxonomy 
  545. * @since 2.3.0 
  546. * 
  547. * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings. 
  548. * 
  549. * @param string|array Taxonomy name or list of Taxonomy names 
  550. * @param string|array $args The values of what to search for when returning terms 
  551. * @return array|WP_Error List of Term Objects and their children. Will return WP_Error, if any of $taxonomies do not exist. 
  552. */ 
  553. function &get_terms($taxonomies, $args = '') { 
  554. $empty_array = array(); 
  555.  
  556. $single_taxonomy = false; 
  557. if ( !is_array($taxonomies) ) { 
  558. $single_taxonomy = true; 
  559. $taxonomies = array($taxonomies); 
  560.  
  561. foreach ( (array) $taxonomies as $taxonomy ) { 
  562. if ( ! $this->is_taxonomy($taxonomy) ) { 
  563. $error = new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 
  564. return $error; 
  565.  
  566. $in_taxonomies = "'" . implode("', '", $taxonomies) . "'"; 
  567.  
  568. $defaults = array('orderby' => 'name', 'order' => 'ASC',  
  569. 'hide_empty' => true, 'exclude' => '', 'exclude_tree' => '', 'include' => '',  
  570. 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '',  
  571. 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '',  
  572. 'pad_counts' => false, 'offset' => '', 'search' => ''); 
  573. $args = wp_parse_args( $args, $defaults ); 
  574. $args['number'] = absint( $args['number'] ); 
  575. $args['offset'] = absint( $args['offset'] ); 
  576. if ( !$single_taxonomy || !$this->is_taxonomy_hierarchical($taxonomies[0]) || 
  577. '' !== $args['parent'] ) { 
  578. $args['child_of'] = 0; 
  579. $args['hierarchical'] = false; 
  580. $args['pad_counts'] = false; 
  581.  
  582. if ( 'all' == $args['get'] ) { 
  583. $args['child_of'] = 0; 
  584. $args['hide_empty'] = 0; 
  585. $args['hierarchical'] = false; 
  586. $args['pad_counts'] = false; 
  587. extract($args, EXTR_SKIP); 
  588.  
  589. if ( $child_of ) { 
  590. $hierarchy = $this->_get_term_hierarchy($taxonomies[0]); 
  591. if ( !isset($hierarchy[$child_of]) ) 
  592. return $empty_array; 
  593.  
  594. if ( $parent ) { 
  595. $hierarchy = $this->_get_term_hierarchy($taxonomies[0]); 
  596. if ( !isset($hierarchy[$parent]) ) 
  597. return $empty_array; 
  598.  
  599. // $args can be whatever, only use the args defined in defaults to compute the key 
  600. $filter_key = ( has_filter('list_terms_exclusions') ) ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : ''; 
  601. $key = md5( serialize( compact(array_keys($defaults)) ) . serialize( $taxonomies ) . $filter_key ); 
  602. $last_changed = wp_cache_get('last_changed', 'terms'); 
  603. if ( !$last_changed ) { 
  604. $last_changed = time(); 
  605. wp_cache_set('last_changed', $last_changed, 'terms'); 
  606. $cache_key = "get_terms:$key:$last_changed"; 
  607. $cache = wp_cache_get( $cache_key, 'terms' ); 
  608. if ( false !== $cache ) { 
  609. $cache = apply_filters('get_terms', $cache, $taxonomies, $args); 
  610. return $cache; 
  611.  
  612. $_orderby = strtolower($orderby); 
  613. if ( 'count' == $_orderby ) 
  614. $orderby = 'tt.count'; 
  615. else if ( 'name' == $_orderby ) 
  616. $orderby = 't.name'; 
  617. else if ( 'slug' == $_orderby ) 
  618. $orderby = 't.slug'; 
  619. else if ( 'term_group' == $_orderby ) 
  620. $orderby = 't.term_group'; 
  621. elseif ( empty($_orderby) || 'id' == $_orderby ) 
  622. $orderby = 't.term_id'; 
  623.  
  624. $orderby = apply_filters( 'get_terms_orderby', $orderby, $args ); 
  625.  
  626. $where = ''; 
  627. $inclusions = ''; 
  628. if ( !empty($include) ) { 
  629. $exclude = ''; 
  630. $exclude_tree = ''; 
  631. $interms = preg_split('/[\s, ]+/', $include); 
  632. if ( count($interms) ) { 
  633. foreach ( (array) $interms as $interm ) { 
  634. if (empty($inclusions)) 
  635. $inclusions = ' AND ( t.term_id = ' . intval($interm) . ' '; 
  636. else 
  637. $inclusions .= ' OR t.term_id = ' . intval($interm) . ' '; 
  638.  
  639. if ( !empty($inclusions) ) 
  640. $inclusions .= ')'; 
  641. $where .= $inclusions; 
  642.  
  643. $exclusions = ''; 
  644. if ( ! empty( $exclude_tree ) ) { 
  645. $excluded_trunks = preg_split('/[\s, ]+/', $exclude_tree); 
  646. foreach( (array) $excluded_trunks as $extrunk ) { 
  647. $excluded_children = (array) $this->get_terms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids')); 
  648. $excluded_children[] = $extrunk; 
  649. foreach( (array) $excluded_children as $exterm ) { 
  650. if ( empty($exclusions) ) 
  651. $exclusions = ' AND ( t.term_id <> ' . intval($exterm) . ' '; 
  652. else 
  653. $exclusions .= ' AND t.term_id <> ' . intval($exterm) . ' '; 
  654.  
  655. if ( !empty($exclude) ) { 
  656. $exterms = preg_split('/[\s, ]+/', $exclude); 
  657. if ( count($exterms) ) { 
  658. foreach ( (array) $exterms as $exterm ) { 
  659. if ( empty($exclusions) ) 
  660. $exclusions = ' AND ( t.term_id <> ' . intval($exterm) . ' '; 
  661. else 
  662. $exclusions .= ' AND t.term_id <> ' . intval($exterm) . ' '; 
  663.  
  664. if ( !empty($exclusions) ) 
  665. $exclusions .= ')'; 
  666. $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args ); 
  667. $where .= $exclusions; 
  668.  
  669. if ( !empty($slug) ) { 
  670. $slug = $this->sanitize_term_slug($slug); 
  671. $where .= " AND t.slug = '$slug'"; 
  672.  
  673. if ( !empty($name__like) ) 
  674. $where .= " AND t.name LIKE '{$name__like}%'"; 
  675.  
  676. if ( '' !== $parent ) { 
  677. $parent = (int) $parent; 
  678. $where .= " AND tt.parent = '$parent'"; 
  679.  
  680. if ( $hide_empty && !$hierarchical ) 
  681. $where .= ' AND tt.count > 0'; 
  682.  
  683. // don't limit the query results when we have to descend the family tree 
  684. if ( ! empty($number) && ! $hierarchical && empty( $child_of ) && '' === $parent ) { 
  685. if( $offset ) 
  686. $limit = 'LIMIT ' . $offset . ', ' . $number; 
  687. else 
  688. $limit = 'LIMIT ' . $number; 
  689.  
  690. } else 
  691. $limit = ''; 
  692.  
  693. if ( !empty($search) ) { 
  694. $search = like_escape($search); 
  695. $where .= " AND (t.name LIKE '%$search%')"; 
  696.  
  697. if ( !in_array( $fields, array( 'all', 'ids', 'names', 'tt_ids' ) ) ) 
  698. $fields = 'all'; 
  699.  
  700. $selects = array(); 
  701. if ( 'all' == $fields ) 
  702. $selects = array('t.*', 'tt.*'); 
  703. else if ( 'ids' == $fields ) 
  704. $selects = array('t.term_id', 'tt.parent', 'tt.count'); 
  705. else if ( 'names' == $fields ) 
  706. $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name'); 
  707. $select_this = implode(', ', apply_filters( 'get_terms_fields', $selects, $args )); 
  708.  
  709. $query = "SELECT $select_this FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ($in_taxonomies) $where ORDER BY $orderby $order $limit"; 
  710.  
  711. $terms = $this->db->get_results($query); 
  712. if ( 'all' == $fields ) { 
  713. $this->update_term_cache($terms); 
  714.  
  715. if ( empty($terms) ) { 
  716. wp_cache_add( $cache_key, array(), 'terms' ); 
  717. $terms = apply_filters('get_terms', array(), $taxonomies, $args); 
  718. return $terms; 
  719.  
  720. if ( $child_of || $hierarchical ) { 
  721. $children = $this->_get_term_hierarchy($taxonomies[0]); 
  722. if ( ! empty($children) ) 
  723. $terms = & $this->_get_term_children($child_of, $terms, $taxonomies[0]); 
  724.  
  725. // Update term counts to include children. 
  726. if ( $pad_counts ) 
  727. $this->_pad_term_counts($terms, $taxonomies[0]); 
  728.  
  729. // Make sure we show empty categories that have children. 
  730. if ( $hierarchical && $hide_empty && is_array($terms) ) { 
  731. foreach ( $terms as $k => $term ) { 
  732. if ( ! $term->count ) { 
  733. $children = $this->_get_term_children($term->term_id, $terms, $taxonomies[0]); 
  734. if( is_array($children) ) 
  735. foreach ( $children as $child ) 
  736. if ( $child->count ) 
  737. continue 2; 
  738.  
  739. // It really is empty 
  740. unset($terms[$k]); 
  741. reset ( $terms ); 
  742.  
  743. $_terms = array(); 
  744. if ( 'ids' == $fields ) { 
  745. while ( $term = array_shift($terms) ) 
  746. $_terms[] = $term->term_id; 
  747. $terms = $_terms; 
  748. } elseif ( 'names' == $fields ) { 
  749. while ( $term = array_shift($terms) ) 
  750. $_terms[] = $term->name; 
  751. $terms = $_terms; 
  752.  
  753. if ( 0 < $number && intval(@count($terms)) > $number ) { 
  754. $terms = array_slice($terms, $offset, $number); 
  755.  
  756. wp_cache_add( $cache_key, $terms, 'terms' ); 
  757.  
  758. $terms = apply_filters('get_terms', $terms, $taxonomies, $args); 
  759. return $terms; 
  760.  
  761. /** 
  762. * Check if Term exists. 
  763. * 
  764. * Returns the index of a defined term, or 0 (false) if the term doesn't exist. 
  765. * 
  766. * @package WordPress 
  767. * @subpackage Taxonomy 
  768. * @since 2.3.0 
  769. * 
  770. * @param int|string $term The term to check 
  771. * @param string $taxonomy The taxonomy name to use 
  772. * @param int $parent ID of parent term under which to confine the exists search. 
  773. * @return mixed Get the term id or Term Object, if exists. 
  774. */ 
  775. function is_term($term, $taxonomy = '', $parent = 0) { 
  776. $select = "SELECT term_id FROM {$this->db->terms} as t WHERE "; 
  777. $tax_select = "SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE "; 
  778.  
  779. if ( is_int($term) ) { 
  780. if ( 0 == $term ) 
  781. return 0; 
  782. $where = 't.term_id = %d'; 
  783. if ( !empty($taxonomy) ) 
  784. return $this->db->get_row( $this->db->prepare( $tax_select . $where . " AND tt.taxonomy = %s", $term, $taxonomy ), ARRAY_A ); 
  785. else 
  786. return $this->db->get_var( $this->db->prepare( $select . $where, $term ) ); 
  787.  
  788. $term = trim( stripslashes( $term ) ); 
  789.  
  790. if ( '' === $slug = $this->sanitize_term_slug($term) ) 
  791. return 0; 
  792.  
  793. $where = 't.slug = %s'; 
  794. $else_where = 't.name = %s'; 
  795. $where_fields = array($slug); 
  796. $else_where_fields = array($term); 
  797. if ( !empty($taxonomy) ) { 
  798. $parent = (int) $parent; 
  799. if ( $parent > 0 ) { 
  800. $where_fields[] = $parent; 
  801. $else_where_fields[] = $parent; 
  802. $where .= ' AND tt.parent = %d'; 
  803. $else_where .= ' AND tt.parent = %d'; 
  804.  
  805. $where_fields[] = $taxonomy; 
  806. $else_where_fields[] = $taxonomy; 
  807.  
  808. if ( $result = $this->db->get_row( $this->db->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE $where AND tt.taxonomy = %s", $where_fields), ARRAY_A) ) 
  809. return $result; 
  810.  
  811. return $this->db->get_row( $this->db->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE $else_where AND tt.taxonomy = %s", $else_where_fields), ARRAY_A); 
  812.  
  813. if ( $result = $this->db->get_var( $this->db->prepare("SELECT term_id FROM {$this->db->terms} as t WHERE $where", $where_fields) ) ) 
  814. return $result; 
  815.  
  816. return $this->db->get_var( $this->db->prepare("SELECT term_id FROM {$this->db->terms} as t WHERE $else_where", $else_where_fields) ); 
  817.  
  818. function sanitize_term_slug( $title, $taxonomy = '', $term_id = 0 ) { 
  819. return apply_filters( 'pre_term_slug', $title, $taxonomy, $term_id ); 
  820.  
  821. function format_to_edit( $text ) { 
  822. return format_to_edit( $text ); 
  823.  
  824. /** 
  825. * Sanitize Term all fields 
  826. * 
  827. * Relies on sanitize_term_field() to sanitize the term. The difference 
  828. * is that this function will sanitize <strong>all</strong> fields. The 
  829. * context is based on sanitize_term_field(). 
  830. * 
  831. * The $term is expected to be either an array or an object. 
  832. * 
  833. * @package WordPress 
  834. * @subpackage Taxonomy 
  835. * @since 2.3.0 
  836. * 
  837. * @uses $this->sanitize_term_field Used to sanitize all fields in a term 
  838. * 
  839. * @param array|object $term The term to check 
  840. * @param string $taxonomy The taxonomy name to use 
  841. * @param string $context Default is 'display'. 
  842. * @return array|object Term with all fields sanitized 
  843. */ 
  844. function sanitize_term($term, $taxonomy, $context = 'display') { 
  845.  
  846. if ( 'raw' == $context ) 
  847. return $term; 
  848.  
  849. $fields = array('term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group'); 
  850.  
  851. $do_object = false; 
  852. if ( is_object($term) ) 
  853. $do_object = true; 
  854.  
  855. $term_id = $do_object ? $term->term_id : (isset($term['term_id']) ? $term['term_id'] : 0); 
  856.  
  857. foreach ( (array) $fields as $field ) { 
  858. if ( $do_object ) { 
  859. if ( isset($term->$field) ) 
  860. $term->$field = $this->sanitize_term_field($field, $term->$field, $term_id, $taxonomy, $context); 
  861. } else { 
  862. if ( isset($term[$field]) ) 
  863. $term[$field] = $this->sanitize_term_field($field, $term[$field], $term_id, $taxonomy, $context); 
  864.  
  865. if ( $do_object ) 
  866. $term->filter = $context; 
  867. else 
  868. $term['filter'] = $context; 
  869.  
  870. return $term; 
  871.  
  872. /** 
  873. * Cleanse the field value in the term based on the context. 
  874. * 
  875. * Passing a term field value through the function should be assumed to have 
  876. * cleansed the value for whatever context the term field is going to be used. 
  877. * 
  878. * If no context or an unsupported context is given, then default filters will 
  879. * be applied. 
  880. * 
  881. * There are enough filters for each context to support a custom filtering 
  882. * without creating your own filter function. Simply create a function that 
  883. * hooks into the filter you need. 
  884. * 
  885. * @package WordPress 
  886. * @subpackage Taxonomy 
  887. * @since 2.3.0 
  888. * 
  889. * @param string $field Term field to sanitize 
  890. * @param string $value Search for this term value 
  891. * @param int $term_id Term ID 
  892. * @param string $taxonomy Taxonomy Name 
  893. * @param string $context Either edit, db, display, attribute, or js. 
  894. * @return mixed sanitized field 
  895. */ 
  896. function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) { 
  897. if ( 'parent' == $field || 'term_id' == $field || 'count' == $field || 'term_group' == $field ) { 
  898. $value = (int) $value; 
  899. if ( $value < 0 ) 
  900. $value = 0; 
  901.  
  902. if ( 'raw' == $context ) 
  903. return $value; 
  904.  
  905. if ( 'edit' == $context ) { 
  906. $value = apply_filters("edit_term_$field", $value, $term_id, $taxonomy); 
  907. $value = apply_filters("edit_${taxonomy}_$field", $value, $term_id); 
  908. if ( 'description' == $field ) 
  909. $value = $this->format_to_edit($value); 
  910. else 
  911. $value = esc_attr($value); 
  912. } else if ( 'db' == $context ) { 
  913. $value = apply_filters("pre_term_$field", $value, $taxonomy); 
  914. $value = apply_filters("pre_${taxonomy}_$field", $value); 
  915. // WP DIFF 
  916. } else if ( 'rss' == $context ) { 
  917. $value = apply_filters("term_${field}_rss", $value, $taxonomy); 
  918. $value = apply_filters("${taxonomy}_${field}_rss", $value); 
  919. } else { 
  920. // Use display filters by default. 
  921. $value = apply_filters("term_$field", $value, $term_id, $taxonomy, $context); 
  922. $value = apply_filters("${taxonomy}_$field", $value, $term_id, $context); 
  923.  
  924. if ( 'attribute' == $context ) 
  925. $value = esc_attr($value); 
  926. else if ( 'js' == $context ) 
  927. $value = esc_js($value); 
  928.  
  929. return $value; 
  930.  
  931. /** 
  932. * Count how many terms are in Taxonomy. 
  933. * 
  934. * Default $args is 'ignore_empty' which can be <code>'ignore_empty=true'</code> 
  935. * or <code>array('ignore_empty' => true);</code>. 
  936. * 
  937. * @package WordPress 
  938. * @subpackage Taxonomy 
  939. * @since 2.3.0 
  940. * 
  941. * @uses wp_parse_args() Turns strings into arrays and merges defaults into an array. 
  942. * 
  943. * @param string $taxonomy Taxonomy name 
  944. * @param array|string $args Overwrite defaults 
  945. * @return int How many terms are in $taxonomy 
  946. */ 
  947. function count_terms( $taxonomy, $args = array() ) { 
  948. $defaults = array('ignore_empty' => false); 
  949. $args = wp_parse_args($args, $defaults); 
  950. extract($args, EXTR_SKIP); 
  951.  
  952. $where = ''; 
  953. if ( $ignore_empty ) 
  954. $where = 'AND count > 0'; 
  955.  
  956. return $this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_taxonomy} WHERE taxonomy = %s $where", $taxonomy ) ); 
  957.  
  958. /** 
  959. * Will unlink the term from the taxonomy. 
  960. * 
  961. * Will remove the term's relationship to the taxonomy, not the term or taxonomy 
  962. * itself. The term and taxonomy will still exist. Will require the term's 
  963. * object ID to perform the operation. 
  964. * 
  965. * @package WordPress 
  966. * @subpackage Taxonomy 
  967. * @since 2.3.0 
  968. * 
  969. * @param int $object_id The term Object Id that refers to the term 
  970. * @param string|array $taxonomy List of Taxonomy Names or single Taxonomy name. 
  971. */ 
  972. function delete_object_term_relationships( $object_id, $taxonomies ) { 
  973. $object_id = (int) $object_id; 
  974.  
  975. if ( !is_array($taxonomies) ) 
  976. $taxonomies = array($taxonomies); 
  977.  
  978. foreach ( (array) $taxonomies as $taxonomy ) { 
  979. $terms = $this->get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids')); 
  980. $in_terms = "'" . implode("', '", $terms) . "'"; 
  981. $this->db->query( $this->db->prepare( "DELETE FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($in_terms)", $object_id ) ); 
  982. $this->update_term_count($terms, $taxonomy); 
  983.  
  984. /** 
  985. * Removes a term from the database. 
  986. * 
  987. * If the term is a parent of other terms, then the children will be updated to 
  988. * that term's parent. 
  989. * 
  990. * The $args 'default' will only override the terms found, if there is only one 
  991. * term found. Any other and the found terms are used. 
  992. * 
  993. * The $args 'force_default' will force the term supplied as default to be 
  994. * assigned even if the object was not going to be termless 
  995. * @package WordPress 
  996. * @subpackage Taxonomy 
  997. * @since 2.3.0 
  998. * 
  999. * @uses do_action() Calls both 'delete_term' and 'delete_$taxonomy' action 
  1000. * hooks, passing term object, term id. 'delete_term' gets an additional 
  1001. * parameter with the $taxonomy parameter. 
  1002. * 
  1003. * @param int $term Term ID 
  1004. * @param string $taxonomy Taxonomy Name 
  1005. * @param array|string $args Optional. Change 'default' term id and override found term ids. 
  1006. * @return bool|WP_Error Returns false if not term; true if completes delete action. 
  1007. */ 
  1008. function delete_term( $term, $taxonomy, $args = array() ) { 
  1009. $term = (int) $term; 
  1010.  
  1011. if ( ! $ids = $this->is_term($term, $taxonomy) ) 
  1012. return false; 
  1013. if ( is_wp_error( $ids ) ) 
  1014. return $ids; 
  1015.  
  1016. $tt_id = $ids['term_taxonomy_id']; 
  1017.  
  1018. $defaults = array(); 
  1019. $args = wp_parse_args($args, $defaults); 
  1020. extract($args, EXTR_SKIP); 
  1021.  
  1022. if ( isset($default) ) { 
  1023. $default = (int) $default; 
  1024. if ( !$this->is_term($default, $taxonomy) ) 
  1025. unset($default); 
  1026.  
  1027. // Update children to point to new parent 
  1028. if ( $this->is_taxonomy_hierarchical($taxonomy) ) { 
  1029. $term_obj = $this->get_term($term, $taxonomy); 
  1030. if ( is_wp_error( $term_obj ) ) 
  1031. return $term_obj; 
  1032. $parent = $term_obj->parent; 
  1033.  
  1034. $this->db->update( $this->db->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id ) + compact( 'taxonomy' ) ); 
  1035.  
  1036. $objects = $this->db->get_col( $this->db->prepare( "SELECT object_id FROM {$this->db->term_relationships} WHERE term_taxonomy_id = %d", $tt_id ) ); 
  1037.  
  1038. foreach ( (array) $objects as $object ) { 
  1039. $terms = $this->get_object_terms($object, $taxonomy, array('fields' => 'ids', 'orderby' => 'none')); 
  1040. if ( 1 == count($terms) && isset($default) ) { 
  1041. $terms = array($default); 
  1042. } else { 
  1043. $terms = array_diff($terms, array($term)); 
  1044. if (isset($default) && isset($force_default) && $force_default) 
  1045. $terms = array_merge($terms, array($default)); 
  1046. $terms = array_map('intval', $terms); 
  1047. $this->set_object_terms($object, $terms, $taxonomy); 
  1048.  
  1049. $this->db->query( $this->db->prepare( "DELETE FROM {$this->db->term_taxonomy} WHERE term_taxonomy_id = %d", $tt_id ) ); 
  1050.  
  1051. // Delete the term if no taxonomies use it. 
  1052. if ( !$this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_taxonomy} WHERE term_id = %d", $term) ) ) 
  1053. $this->db->query( $this->db->prepare( "DELETE FROM {$this->db->terms} WHERE term_id = %d", $term) ); 
  1054.  
  1055. $this->clean_term_cache($term, $taxonomy); 
  1056.  
  1057. do_action('delete_term', $term, $tt_id, $taxonomy); 
  1058. do_action("delete_$taxonomy", $term, $tt_id); 
  1059.  
  1060. return true; 
  1061.  
  1062. /** 
  1063. * Retrieves the terms associated with the given object(s), in the supplied taxonomies. 
  1064. * 
  1065. * The following information has to do the $args parameter and for what can be 
  1066. * contained in the string or array of that parameter, if it exists. 
  1067. * 
  1068. * The first argument is called, 'orderby' and has the default value of 'name'. 
  1069. * The other value that is supported is 'count'. 
  1070. * 
  1071. * The second argument is called, 'order' and has the default value of 'ASC'. 
  1072. * The only other value that will be acceptable is 'DESC'. 
  1073. * 
  1074. * The final argument supported is called, 'fields' and has the default value of 
  1075. * 'all'. There are multiple other options that can be used instead. Supported 
  1076. * values are as follows: 'all', 'ids', 'names', and finally 
  1077. * 'all_with_object_id'. 
  1078. * 
  1079. * The fields argument also decides what will be returned. If 'all' or 
  1080. * 'all_with_object_id' is choosen or the default kept intact, then all matching 
  1081. * terms objects will be returned. If either 'ids' or 'names' is used, then an 
  1082. * array of all matching term ids or term names will be returned respectively. 
  1083. * 
  1084. * @package WordPress 
  1085. * @subpackage Taxonomy 
  1086. * @since 2.3.0 
  1087. * 
  1088. * @param int|array $object_id The id of the object(s) to retrieve. 
  1089. * @param string|array $taxonomies The taxonomies to retrieve terms from. 
  1090. * @param array|string $args Change what is returned 
  1091. * @return array|WP_Error The requested term data or empty array if no terms found. WP_Error if $taxonomy does not exist. 
  1092. */ 
  1093. function get_object_terms($object_ids, $taxonomies, $args = array()) { 
  1094. if ( !is_array($taxonomies) ) 
  1095. $taxonomies = array($taxonomies); 
  1096.  
  1097. foreach ( (array) $taxonomies as $taxonomy ) { 
  1098. if ( !$this->is_taxonomy($taxonomy) ) 
  1099. return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 
  1100.  
  1101. if ( !is_array($object_ids) ) 
  1102. $object_ids = array($object_ids); 
  1103. $object_ids = array_map('intval', $object_ids); 
  1104.  
  1105. $defaults = array('orderby' => 'name', 'order' => 'ASC', 'fields' => 'all'); 
  1106. $args = wp_parse_args( $args, $defaults ); 
  1107.  
  1108. $terms = array(); 
  1109. if ( count($taxonomies) > 1 ) { 
  1110. foreach ( $taxonomies as $index => $taxonomy ) { 
  1111. $t = $this->get_taxonomy($taxonomy); 
  1112. if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) { 
  1113. unset($taxonomies[$index]); 
  1114. $terms = array_merge($terms, $this->get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args))); 
  1115. } else { 
  1116. $t = $this->get_taxonomy($taxonomies[0]); 
  1117. if ( isset($t->args) && is_array($t->args) ) 
  1118. $args = array_merge($args, $t->args); 
  1119.  
  1120. extract($args, EXTR_SKIP); 
  1121.  
  1122. if ( 'count' == $orderby ) 
  1123. $orderby = 'tt.count'; 
  1124. else if ( 'name' == $orderby ) 
  1125. $orderby = 't.name'; 
  1126. else if ( 'slug' == $orderby ) 
  1127. $orderby = 't.slug'; 
  1128. else if ( 'term_group' == $orderby ) 
  1129. $orderby = 't.term_group'; 
  1130. else if ( 'term_order' == $orderby ) 
  1131. $orderby = 'tr.term_order'; 
  1132. else if ( 'none' == $orderby ) { 
  1133. $orderby = ''; 
  1134. $order = ''; 
  1135. } else { 
  1136. $orderby = 't.term_id'; 
  1137.  
  1138. // tt_ids queries can only be none or tr.term_taxonomy_id 
  1139. if ( ('tt_ids' == $fields) && !empty($orderby) ) 
  1140. $orderby = 'tr.term_taxonomy_id'; 
  1141.  
  1142. if ( !empty($orderby) ) 
  1143. $orderby = "ORDER BY $orderby"; 
  1144.  
  1145. $taxonomies = "'" . implode("', '", $taxonomies) . "'"; 
  1146. $object_ids = implode(', ', $object_ids); 
  1147.  
  1148. $select_this = ''; 
  1149. if ( 'all' == $fields ) 
  1150. $select_this = 't.*, tt.*'; 
  1151. else if ( 'ids' == $fields ) 
  1152. $select_this = 't.term_id'; 
  1153. else if ( 'names' == $fields ) 
  1154. $select_this = 't.name'; 
  1155. else if ( 'all_with_object_id' == $fields ) 
  1156. $select_this = 't.*, tt.*, tr.object_id'; 
  1157.  
  1158. $query = "SELECT $select_this FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON tt.term_id = t.term_id INNER JOIN {$this->db->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tr.object_id IN ($object_ids) $orderby $order"; 
  1159.  
  1160. if ( 'all' == $fields || 'all_with_object_id' == $fields ) { 
  1161. $terms = array_merge($terms, $this->db->get_results($query)); 
  1162. $this->update_term_cache($terms); 
  1163. } else if ( 'ids' == $fields || 'names' == $fields ) { 
  1164. $terms = array_merge($terms, $this->db->get_col($query)); 
  1165. } else if ( 'tt_ids' == $fields ) { 
  1166. $terms = $this->db->get_col("SELECT tr.term_taxonomy_id FROM {$this->db->term_relationships} AS tr INNER JOIN {$this->db->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tr.object_id IN ($object_ids) AND tt.taxonomy IN ($taxonomies) $orderby $order"); 
  1167.  
  1168. if ( ! $terms ) 
  1169. $terms = array(); 
  1170.  
  1171. return apply_filters('wp_get_object_terms', $terms, $object_ids, $taxonomies, $args); 
  1172.  
  1173. /** 
  1174. * Adds a new term to the database. Optionally marks it as an alias of an existing term. 
  1175. * 
  1176. * Error handling is assigned for the nonexistance of the $taxonomy and $term 
  1177. * parameters before inserting. If both the term id and taxonomy exist 
  1178. * previously, then an array will be returned that contains the term id and the 
  1179. * contents of what is returned. The keys of the array are 'term_id' and 
  1180. * 'term_taxonomy_id' containing numeric values. 
  1181. * 
  1182. * It is assumed that the term does not yet exist or the above will apply. The 
  1183. * term will be first added to the term table and then related to the taxonomy 
  1184. * if everything is well. If everything is correct, then several actions will be 
  1185. * run prior to a filter and then several actions will be run after the filter 
  1186. * is run. 
  1187. * 
  1188. * The arguments decide how the term is handled based on the $args parameter. 
  1189. * The following is a list of the available overrides and the defaults. 
  1190. * 
  1191. * 'alias_of'. There is no default, but if added, expected is the slug that the 
  1192. * term will be an alias of. Expected to be a string. 
  1193. * 
  1194. * 'description'. There is no default. If exists, will be added to the database 
  1195. * along with the term. Expected to be a string. 
  1196. * 
  1197. * 'parent'. Expected to be numeric and default is 0 (zero). Will assign value 
  1198. * of 'parent' to the term. 
  1199. * 
  1200. * 'slug'. Expected to be a string. There is no default. 
  1201. * 
  1202. * If 'slug' argument exists then the slug will be checked to see if it is not 
  1203. * a valid term. If that check succeeds (it is not a valid term), then it is 
  1204. * added and the term id is given. If it fails, then a check is made to whether 
  1205. * the taxonomy is hierarchical and the parent argument is not empty. If the 
  1206. * second check succeeds, the term will be inserted and the term id will be 
  1207. * given. 
  1208. * 
  1209. * @package WordPress 
  1210. * @subpackage Taxonomy 
  1211. * @since 2.3.0 
  1212. * 
  1213. * @uses do_action() Calls 'create_term' hook with the term id and taxonomy id as parameters. 
  1214. * @uses do_action() Calls 'create_$taxonomy' hook with term id and taxonomy id as parameters. 
  1215. * @uses apply_filters() Calls 'term_id_filter' hook with term id and taxonomy id as parameters. 
  1216. * @uses do_action() Calls 'created_term' hook with the term id and taxonomy id as parameters. 
  1217. * @uses do_action() Calls 'created_$taxonomy' hook with term id and taxonomy id as parameters. 
  1218. * 
  1219. * @param int|string $term The term to add or update. 
  1220. * @param string $taxonomy The taxonomy to which to add the term 
  1221. * @param array|string $args Change the values of the inserted term 
  1222. * @return array|WP_Error The Term ID and Term Taxonomy ID 
  1223. */ 
  1224. function insert_term( $term, $taxonomy, $args = array() ) { 
  1225. if ( !$this->is_taxonomy($taxonomy) ) 
  1226. return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); 
  1227.  
  1228. if ( is_int($term) && 0 == $term ) 
  1229. return new WP_Error('invalid_term_id', __('Invalid term ID')); 
  1230.  
  1231. if ( '' == trim($term) ) 
  1232. return new WP_Error('empty_term_name', __('A name is required for this term')); 
  1233.  
  1234. $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); 
  1235. $args = wp_parse_args($args, $defaults); 
  1236. $args['name'] = $term; 
  1237. $args['taxonomy'] = $taxonomy; 
  1238. $args = $this->sanitize_term($args, $taxonomy, 'db'); 
  1239. extract($args, EXTR_SKIP); 
  1240.  
  1241. // expected_slashed ($name) 
  1242. $name = stripslashes($name); 
  1243. $description = stripslashes($description); 
  1244.  
  1245. if ( empty($slug) ) 
  1246. $slug = $this->sanitize_term_slug($name, $taxonomy); 
  1247.  
  1248. $term_group = 0; 
  1249. if ( $alias_of ) { 
  1250. $alias = $this->db->get_row( $this->db->prepare( "SELECT term_id, term_group FROM {$this->db->terms} WHERE slug = %s", $alias_of) ); 
  1251. if ( $alias->term_group ) { 
  1252. // The alias we want is already in a group, so let's use that one. 
  1253. $term_group = $alias->term_group; 
  1254. } else { 
  1255. // The alias isn't in a group, so let's create a new one and firstly add the alias term to it. 
  1256. $term_group = $this->db->get_var("SELECT MAX(term_group) FROM {$this->db->terms}") + 1; 
  1257. $this->db->query( $this->db->prepare( "UPDATE {$this->db->terms} SET term_group = %d WHERE term_id = %d", $term_group, $alias->term_id ) ); 
  1258.  
  1259. if ( ! $term_id = $this->is_term($slug) ) { 
  1260. if ( false === $this->db->insert( $this->db->terms, compact( 'name', 'slug', 'term_group' ) ) ) 
  1261. return new WP_Error('db_insert_error', __('Could not insert term into the database'), $this->db->last_error); 
  1262. $term_id = (int) $this->db->insert_id; 
  1263. } else if ( $this->is_taxonomy_hierarchical($taxonomy) && !empty($parent) ) { 
  1264. // If the taxonomy supports hierarchy and the term has a parent, make the slug unique 
  1265. // by incorporating parent slugs. 
  1266. $slug = $this->unique_term_slug($slug, (object) $args); 
  1267. if ( false === $this->db->insert( $this->db->terms, compact( 'name', 'slug', 'term_group' ) ) ) 
  1268. return new WP_Error('db_insert_error', __('Could not insert term into the database'), $this->db->last_error); 
  1269. $term_id = (int) $this->db->insert_id; 
  1270.  
  1271. if ( empty($slug) ) { 
  1272. $slug = $this->sanitize_term_slug($slug, $taxonomy, $term_id); 
  1273. $this->db->update( $this->db->terms, compact( 'slug' ), compact( 'term_id' ) ); 
  1274.  
  1275. $tt_id = $this->db->get_var( $this->db->prepare( "SELECT tt.term_taxonomy_id FROM {$this->db->term_taxonomy} AS tt INNER JOIN {$this->db->terms} AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id ) ); 
  1276.  
  1277. if ( !empty($tt_id) ) 
  1278. return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 
  1279.  
  1280. $this->db->insert( $this->db->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) ); 
  1281. $tt_id = (int) $this->db->insert_id; 
  1282.  
  1283. do_action("create_term", $term_id, $tt_id); 
  1284. do_action("create_$taxonomy", $term_id, $tt_id); 
  1285.  
  1286. $term_id = apply_filters('term_id_filter', $term_id, $tt_id); 
  1287.  
  1288. $this->clean_term_cache($term_id, $taxonomy); 
  1289.  
  1290. do_action("created_term", $term_id, $tt_id); 
  1291. do_action("created_$taxonomy", $term_id, $tt_id); 
  1292.  
  1293. return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 
  1294.  
  1295. /** 
  1296. * Create Term and Taxonomy Relationships. 
  1297. * 
  1298. * Relates an object (post, link etc) to a term and taxonomy type. Creates the 
  1299. * term and taxonomy relationship if it doesn't already exist. Creates a term if 
  1300. * it doesn't exist (using the slug). 
  1301. * 
  1302. * A relationship means that the term is grouped in or belongs to the taxonomy. 
  1303. * A term has no meaning until it is given context by defining which taxonomy it 
  1304. * exists under. 
  1305. * 
  1306. * @package WordPress 
  1307. * @subpackage Taxonomy 
  1308. * @since 2.3.0 
  1309. * 
  1310. * @param int $object_id The object to relate to. 
  1311. * @param array|int|string $term The slug or id of the term, will replace all existing 
  1312. * related terms in this taxonomy. 
  1313. * @param array|string $taxonomy The context in which to relate the term to the object. 
  1314. * @param bool $append If false will delete difference of terms. 
  1315. * @return array|WP_Error Affected Term IDs 
  1316. */ 
  1317. function set_object_terms($object_id, $terms, $taxonomy, $append = false) { 
  1318. $object_id = (int) $object_id; 
  1319.  
  1320. if ( !$this->is_taxonomy($taxonomy) ) 
  1321. return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 
  1322.  
  1323. if ( !is_array($terms) ) 
  1324. $terms = array($terms); 
  1325.  
  1326. if ( ! $append ) 
  1327. $old_tt_ids = $this->get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids', 'orderby' => 'none')); 
  1328.  
  1329. $tt_ids = array(); 
  1330. $term_ids = array(); 
  1331.  
  1332. foreach ( (array) $terms as $term ) { 
  1333. if ( !strlen(trim($term)) ) 
  1334. continue; 
  1335.  
  1336. if ( !$id = $this->is_term($term, $taxonomy) ) 
  1337. $id = $this->insert_term($term, $taxonomy); 
  1338. if ( is_wp_error($id) ) 
  1339. return $id; 
  1340. $term_ids[] = $id['term_id']; 
  1341. $id = $id['term_taxonomy_id']; 
  1342. $tt_ids[] = $id; 
  1343.  
  1344. if ( $this->db->get_var( $this->db->prepare( "SELECT term_taxonomy_id FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id = %d", $object_id, $id ) ) ) 
  1345. continue; 
  1346. $this->db->insert( $this->db->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $id ) ); 
  1347.  
  1348. $this->update_term_count($tt_ids, $taxonomy); 
  1349.  
  1350. if ( ! $append ) { 
  1351. $delete_terms = array_diff($old_tt_ids, $tt_ids); 
  1352. if ( $delete_terms ) { 
  1353. $in_delete_terms = "'" . implode("', '", $delete_terms) . "'"; 
  1354. $this->db->query( $this->db->prepare("DELETE FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($in_delete_terms)", $object_id) ); 
  1355. $this->update_term_count($delete_terms, $taxonomy); 
  1356.  
  1357. $t = $this->get_taxonomy($taxonomy); 
  1358. if ( ! $append && isset($t->sort) && $t->sort ) { 
  1359. $values = array(); 
  1360. $term_order = 0; 
  1361. $final_tt_ids = $this->get_object_terms($object_id, $taxonomy, 'fields=tt_ids'); 
  1362. foreach ( $tt_ids as $tt_id ) 
  1363. if ( in_array($tt_id, $final_tt_ids) ) 
  1364. $values[] = $this->db->prepare( "(%d, %d, %d)", $object_id, $tt_id, ++$term_order); 
  1365. if ( $values ) 
  1366. $this->db->query("INSERT INTO {$this->db->term_relationships} (object_id, term_taxonomy_id, term_order) VALUES " . join(', ', $values) . " ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)"); 
  1367.  
  1368. do_action('set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append); 
  1369. return $tt_ids; 
  1370.  
  1371. /** 
  1372. * Will make slug unique, if it isn't already. 
  1373. * 
  1374. * The $slug has to be unique global to every taxonomy, meaning that one 
  1375. * taxonomy term can't have a matching slug with another taxonomy term. Each 
  1376. * slug has to be globally unique for every taxonomy. 
  1377. * 
  1378. * The way this works is that if the taxonomy that the term belongs to is 
  1379. * heirarchical and has a parent, it will append that parent to the $slug. 
  1380. * 
  1381. * If that still doesn't return an unique slug, then it try to append a number 
  1382. * until it finds a number that is truely unique. 
  1383. * 
  1384. * The only purpose for $term is for appending a parent, if one exists. 
  1385. * 
  1386. * @package WordPress 
  1387. * @subpackage Taxonomy 
  1388. * @since 2.3.0 
  1389. * 
  1390. * @param string $slug The string that will be tried for a unique slug 
  1391. * @param object $term The term object that the $slug will belong too 
  1392. * @return string Will return a true unique slug. 
  1393. */ 
  1394. function unique_term_slug($slug, $term) { 
  1395. // If the taxonomy supports hierarchy and the term has a parent, make the slug unique 
  1396. // by incorporating parent slugs. 
  1397. if ( $this->is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) { 
  1398. $the_parent = $term->parent; 
  1399. while ( ! empty($the_parent) ) { 
  1400. $parent_term = $this->get_term($the_parent, $term->taxonomy); 
  1401. if ( is_wp_error($parent_term) || empty($parent_term) ) 
  1402. break; 
  1403. $slug .= '-' . $parent_term->slug; 
  1404. if ( empty($parent_term->parent) ) 
  1405. break; 
  1406. $the_parent = $parent_term->parent; 
  1407.  
  1408. // If we didn't get a unique slug, try appending a number to make it unique. 
  1409. if ( !empty($args['term_id']) ) 
  1410. $query = $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s AND term_id != %d", $slug, $args['term_id'] ); 
  1411. else 
  1412. $query = $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s", $slug ); 
  1413.  
  1414. if ( $this->db->get_var( $query ) ) { 
  1415. $num = 2; 
  1416. do { 
  1417. $alt_slug = $slug . "-$num"; 
  1418. $num++; 
  1419. $slug_check = $this->db->get_var( $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s", $alt_slug ) ); 
  1420. } while ( $slug_check ); 
  1421. $slug = $alt_slug; 
  1422.  
  1423. return $slug; 
  1424.  
  1425. /** 
  1426. * Update term based on arguments provided. 
  1427. * 
  1428. * The $args will indiscriminately override all values with the same field name. 
  1429. * Care must be taken to not override important information need to update or 
  1430. * update will fail (or perhaps create a new term, neither would be acceptable). 
  1431. * 
  1432. * Defaults will set 'alias_of', 'description', 'parent', and 'slug' if not 
  1433. * defined in $args already. 
  1434. * 
  1435. * 'alias_of' will create a term group, if it doesn't already exist, and update 
  1436. * it for the $term. 
  1437. * 
  1438. * If the 'slug' argument in $args is missing, then the 'name' in $args will be 
  1439. * used. It should also be noted that if you set 'slug' and it isn't unique then 
  1440. * a WP_Error will be passed back. If you don't pass any slug, then a unique one 
  1441. * will be created for you. 
  1442. * 
  1443. * For what can be overrode in $args, check the term scheme can contain and stay 
  1444. * away from the term keys. 
  1445. * 
  1446. * @package WordPress 
  1447. * @subpackage Taxonomy 
  1448. * @since 2.3.0 
  1449. * 
  1450. * @uses do_action() Will call both 'edit_term' and 'edit_$taxonomy' twice. 
  1451. * @uses apply_filters() Will call the 'term_id_filter' filter and pass the term 
  1452. * id and taxonomy id. 
  1453. * 
  1454. * @param int $term_id The ID of the term 
  1455. * @param string $taxonomy The context in which to relate the term to the object. 
  1456. * @param array|string $args Overwrite term field values 
  1457. * @return array|WP_Error Returns Term ID and Taxonomy Term ID 
  1458. */ 
  1459. function update_term( $term_id, $taxonomy, $args = array() ) { 
  1460. if ( !$this->is_taxonomy($taxonomy) ) 
  1461. return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); 
  1462.  
  1463. $term_id = (int) $term_id; 
  1464.  
  1465. // First, get all of the original args 
  1466. $term = $this->get_term($term_id, $taxonomy, ARRAY_A); 
  1467.  
  1468. if ( is_wp_error( $term ) ) 
  1469. return $term; 
  1470.  
  1471. // Merge old and new args with new args overwriting old ones. 
  1472. $args = array_merge($term, $args); 
  1473.  
  1474. $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); 
  1475. $args = wp_parse_args($args, $defaults); 
  1476. $args = $this->sanitize_term($args, $taxonomy, 'db'); 
  1477. extract($args, EXTR_SKIP); 
  1478.  
  1479. // expected_slashed ($name) 
  1480. $name = stripslashes($name); 
  1481. $description = stripslashes($description); 
  1482.  
  1483. if ( '' == trim($name) ) 
  1484. return new WP_Error('empty_term_name', __('A name is required for this term')); 
  1485.  
  1486. $empty_slug = false; 
  1487. if ( empty($slug) ) { 
  1488. $empty_slug = true; 
  1489. $slug = $this->sanitize_term_slug($name, $taxonomy, $term_id); 
  1490.  
  1491. if ( $alias_of ) { 
  1492. $alias = $this->db->get_row( $this->db->prepare( "SELECT term_id, term_group FROM {$this->db->terms} WHERE slug = %s", $alias_of) ); 
  1493. if ( $alias->term_group ) { 
  1494. // The alias we want is already in a group, so let's use that one. 
  1495. $term_group = $alias->term_group; 
  1496. } else { 
  1497. // The alias isn't in a group, so let's create a new one and firstly add the alias term to it. 
  1498. $term_group = $this->db->get_var("SELECT MAX(term_group) FROM {$this->db->terms}") + 1; 
  1499. $this->db->update( $this->db->terms, compact('term_group'), array( 'term_id' => $alias->term_id ) ); 
  1500.  
  1501. // Check for duplicate slug 
  1502. $id = $this->db->get_var( $this->db->prepare( "SELECT term_id FROM {$this->db->terms} WHERE slug = %s", $slug ) ); 
  1503. if ( $id && ($id != $term_id) ) { 
  1504. // If an empty slug was passed or the parent changed, reset the slug to something unique. 
  1505. // Otherwise, bail. 
  1506. if ( $empty_slug || ( $parent != $term->parent) ) 
  1507. $slug = $this->unique_term_slug($slug, (object) $args); 
  1508. else 
  1509. return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug)); 
  1510.  
  1511. $this->db->update($this->db->terms, compact( 'name', 'slug', 'term_group' ), compact( 'term_id' ) ); 
  1512.  
  1513. if ( empty($slug) ) { 
  1514. $slug = $this->sanitize_term_slug($name, $taxonomy, $term_id); 
  1515. $this->db->update( $this->db->terms, compact( 'slug' ), compact( 'term_id' ) ); 
  1516.  
  1517. $tt_id = $this->db->get_var( $this->db->prepare( "SELECT tt.term_taxonomy_id FROM {$this->db->term_taxonomy} AS tt INNER JOIN {$this->db->terms} AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) ); 
  1518.  
  1519. $this->db->update( $this->db->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent' ), array( 'term_taxonomy_id' => $tt_id ) ); 
  1520.  
  1521. do_action("edit_term", $term_id, $tt_id); 
  1522. do_action("edit_$taxonomy", $term_id, $tt_id); 
  1523.  
  1524. $term_id = apply_filters('term_id_filter', $term_id, $tt_id); 
  1525.  
  1526. $this->clean_term_cache($term_id, $taxonomy); 
  1527.  
  1528. do_action("edited_term", $term_id, $tt_id); 
  1529. do_action("edited_$taxonomy", $term_id, $tt_id); 
  1530.  
  1531. return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 
  1532.  
  1533. /** 
  1534. * Enable or disable term counting. 
  1535. * 
  1536. * @since 2.5.0 
  1537. * 
  1538. * @param bool $defer Optional. Enable if true, disable if false. 
  1539. * @return bool Whether term counting is enabled or disabled. 
  1540. */ 
  1541. function defer_term_counting($defer=NULL) { 
  1542. static $_defer = false; 
  1543.  
  1544. if ( is_bool($defer) ) { 
  1545. $_defer = $defer; 
  1546. // flush any deferred counts 
  1547. if ( !$defer ) 
  1548. $this->update_term_count( NULL, NULL, true ); 
  1549.  
  1550. return $_defer; 
  1551.  
  1552. /** 
  1553. * Updates the amount of terms in taxonomy. 
  1554. * 
  1555. * If there is a taxonomy callback applied, then it will be called for updating 
  1556. * the count. 
  1557. * 
  1558. * The default action is to count what the amount of terms have the relationship 
  1559. * of term ID. Once that is done, then update the database. 
  1560. * 
  1561. * @package WordPress 
  1562. * @subpackage Taxonomy 
  1563. * @since 2.3.0 
  1564. * @uses $this->db 
  1565. * 
  1566. * @param int|array $terms The ID of the terms 
  1567. * @param string $taxonomy The context of the term. 
  1568. * @return bool If no terms will return false, and if successful will return true. 
  1569. */ 
  1570. function update_term_count( $terms, $taxonomy, $do_deferred=false ) { 
  1571. static $_deferred = array(); 
  1572.  
  1573. if ( $do_deferred ) { 
  1574. foreach ( (array) array_keys($_deferred) as $tax ) { 
  1575. $this->update_term_count_now( $_deferred[$tax], $tax ); 
  1576. unset( $_deferred[$tax] ); 
  1577.  
  1578. if ( empty($terms) ) 
  1579. return false; 
  1580.  
  1581. if ( !is_array($terms) ) 
  1582. $terms = array($terms); 
  1583.  
  1584. if ( $this->defer_term_counting() ) { 
  1585. if ( !isset($_deferred[$taxonomy]) ) 
  1586. $_deferred[$taxonomy] = array(); 
  1587. $_deferred[$taxonomy] = array_unique( array_merge($_deferred[$taxonomy], $terms) ); 
  1588. return true; 
  1589.  
  1590. return $this->update_term_count_now( $terms, $taxonomy ); 
  1591.  
  1592. /** 
  1593. * Perform term count update immediately. 
  1594. * 
  1595. * @since 2.5.0 
  1596. * 
  1597. * @param array $terms The term_taxonomy_id of terms to update. 
  1598. * @param string $taxonomy The context of the term. 
  1599. * @return bool Always true when complete. 
  1600. */ 
  1601. function update_term_count_now( $terms, $taxonomy ) { 
  1602. $terms = array_map('intval', $terms); 
  1603.  
  1604. $taxonomy = $this->get_taxonomy($taxonomy); 
  1605. if ( !empty($taxonomy->update_count_callback) ) { 
  1606. call_user_func($taxonomy->update_count_callback, $terms); 
  1607. } else { 
  1608. // Default count updater 
  1609. foreach ( (array) $terms as $term ) { 
  1610. $count = $this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_relationships} WHERE term_taxonomy_id = %d", $term) ); 
  1611. $this->db->update( $this->db->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); 
  1612.  
  1613.  
  1614. $this->clean_term_cache($terms); 
  1615.  
  1616. return true; 
  1617.  
  1618. // 
  1619.   // Cache 
  1620. // 
  1621.   
  1622. /** 
  1623. * Removes the taxonomy relationship to terms from the cache. 
  1624. * 
  1625. * Will remove the entire taxonomy relationship containing term $object_id. The 
  1626. * term IDs have to exist within the taxonomy $object_type for the deletion to 
  1627. * take place. 
  1628. * 
  1629. * @package WordPress 
  1630. * @subpackage Taxonomy 
  1631. * @since 2.3 
  1632. * 
  1633. * @see $this->get_object_taxonomies() for more on $object_type 
  1634. * @uses do_action() Will call action hook named, 'clean_object_term_cache' after completion. 
  1635. * Passes, function params in same order. 
  1636. * 
  1637. * @param int|array $object_ids Single or list of term object ID(s) 
  1638. * @param string $object_type The taxonomy object type 
  1639. */ 
  1640. function clean_object_term_cache($object_ids, $object_type) { 
  1641. if ( !is_array($object_ids) ) 
  1642. $object_ids = array($object_ids); 
  1643.  
  1644. foreach ( $object_ids as $id ) 
  1645. foreach ( $this->get_object_taxonomies($object_type) as $taxonomy ) 
  1646. wp_cache_delete($id, "{$taxonomy}_relationships"); 
  1647.  
  1648. do_action('clean_object_term_cache', $object_ids, $object_type); 
  1649.  
  1650. /** 
  1651. * Will remove all of the term ids from the cache. 
  1652. * 
  1653. * @package WordPress 
  1654. * @subpackage Taxonomy 
  1655. * @since 2.3.0 
  1656. * 
  1657. * @param int|array $ids Single or list of Term IDs 
  1658. * @param string $taxonomy Can be empty and will assume tt_ids, else will use for context. 
  1659. */ 
  1660. function clean_term_cache($ids, $taxonomy = '') { 
  1661. static $cleaned = array(); 
  1662.  
  1663. if ( !is_array($ids) ) 
  1664. $ids = array($ids); 
  1665.  
  1666. $taxonomies = array(); 
  1667. // If no taxonomy, assume tt_ids. 
  1668. if ( empty($taxonomy) ) { 
  1669. $tt_ids = implode( ', ', array_map( 'intval', $ids ) ); 
  1670. $terms = $this->db->get_results("SELECT term_id, term_taxonomy_id, taxonomy FROM {$this->db->term_taxonomy} WHERE term_taxonomy_id IN ($tt_ids)"); 
  1671. foreach ( (array) $terms as $term ) { 
  1672. $taxonomies[] = $term->taxonomy; 
  1673. wp_cache_delete($term->term_id, $term->taxonomy); 
  1674. wp_cache_delete($term->term_taxonomy_id, "{$term->taxonomy}:tt_id"); 
  1675. $taxonomies = array_unique($taxonomies); 
  1676. } else { 
  1677. $tt_ids = implode( ', ', array_map( 'intval', $ids ) ); 
  1678. $terms = $this->db->get_results("SELECT term_id, term_taxonomy_id FROM {$this->db->term_taxonomy} WHERE term_id IN ($tt_ids)"); 
  1679. foreach ( (array) $terms as $term ) { 
  1680. wp_cache_delete($term->term_id, $taxonomy); 
  1681. wp_cache_delete($term->term_taxonomy_id, "$taxonomy:tt_id"); 
  1682. $taxonomies = array($taxonomy); 
  1683.  
  1684. foreach ( $taxonomies as $taxonomy ) { 
  1685. if ( isset($cleaned[$taxonomy]) ) 
  1686. continue; 
  1687. $cleaned[$taxonomy] = true; 
  1688. wp_cache_delete('all_ids', $taxonomy); 
  1689. wp_cache_delete('get', $taxonomy); 
  1690. $this->delete_children_cache($taxonomy); 
  1691.  
  1692. wp_cache_delete('get_terms', 'terms'); 
  1693.  
  1694. do_action('clean_term_cache', $ids, $taxonomy); 
  1695.  
  1696. /** 
  1697. * Retrieves the taxonomy relationship to the term object id. 
  1698. * 
  1699. * @package WordPress 
  1700. * @subpackage Taxonomy 
  1701. * @since 2.3.0 
  1702. * 
  1703. * @uses wp_cache_get() Retrieves taxonomy relationship from cache 
  1704. * 
  1705. * @param int|array $id Term object ID 
  1706. * @param string $taxonomy Taxonomy Name 
  1707. * @return bool|array Empty array if $terms found, but not $taxonomy. False if nothing is in cache for $taxonomy and $id. 
  1708. */ 
  1709. function &get_object_term_cache($id, $taxonomy) { 
  1710. $cache = wp_cache_get($id, "{$taxonomy}_relationships"); 
  1711. return $cache; 
  1712.  
  1713. /** 
  1714. * Updates the cache for Term ID(s). 
  1715. * 
  1716. * Will only update the cache for terms not already cached. 
  1717. * 
  1718. * The $object_ids expects that the ids be separated by commas, if it is a 
  1719. * string. 
  1720. * 
  1721. * It should be noted that update_object_term_cache() is very time extensive. It 
  1722. * is advised that the function is not called very often or at least not for a 
  1723. * lot of terms that exist in a lot of taxonomies. The amount of time increases 
  1724. * for each term and it also increases for each taxonomy the term belongs to. 
  1725. * 
  1726. * @package WordPress 
  1727. * @subpackage Taxonomy 
  1728. * @since 2.3.0 
  1729. * @uses $this->get_object_terms() Used to get terms from the database to update 
  1730. * 
  1731. * @param string|array $object_ids Single or list of term object ID(s) 
  1732. * @param string $object_type The taxonomy object type 
  1733. * @return null|bool Null value is given with empty $object_ids. False if  
  1734. */ 
  1735. function update_object_term_cache($object_ids, $object_type) { 
  1736. if ( empty($object_ids) ) 
  1737. return; 
  1738.  
  1739. if ( !is_array($object_ids) ) 
  1740. $object_ids = explode(', ', $object_ids); 
  1741.  
  1742. $object_ids = array_map('intval', $object_ids); 
  1743.  
  1744. $taxonomies = $this->get_object_taxonomies($object_type); 
  1745.  
  1746. $ids = array(); 
  1747. foreach ( (array) $object_ids as $id ) { 
  1748. foreach ( $taxonomies as $taxonomy ) { 
  1749. if ( false === wp_cache_get($id, "{$taxonomy}_relationships") ) { 
  1750. $ids[] = $id; 
  1751. break; 
  1752.  
  1753. if ( empty( $ids ) ) 
  1754. return false; 
  1755.  
  1756. $terms = $this->get_object_terms($ids, $taxonomies, 'fields=all_with_object_id'); 
  1757.  
  1758. $object_terms = array(); 
  1759. foreach ( (array) $terms as $term ) 
  1760. $object_terms[$term->object_id][$term->taxonomy][$term->term_id] = $term; 
  1761.  
  1762. foreach ( $ids as $id ) { 
  1763. foreach ( $taxonomies as $taxonomy ) { 
  1764. if ( ! isset($object_terms[$id][$taxonomy]) ) { 
  1765. if ( !isset($object_terms[$id]) ) 
  1766. $object_terms[$id] = array(); 
  1767. $object_terms[$id][$taxonomy] = array(); 
  1768.  
  1769. foreach ( $object_terms as $id => $value ) { 
  1770. foreach ( $value as $taxonomy => $terms ) { 
  1771. wp_cache_set($id, $terms, "{$taxonomy}_relationships"); 
  1772.  
  1773. /** 
  1774. * Updates Terms to Taxonomy in cache. 
  1775. * 
  1776. * @package WordPress 
  1777. * @subpackage Taxonomy 
  1778. * @since 2.3.0 
  1779. * 
  1780. * @param array $terms List of Term objects to change 
  1781. * @param string $taxonomy Optional. Update Term to this taxonomy in cache 
  1782. */ 
  1783. function update_term_cache($terms, $taxonomy = '') { 
  1784. foreach ( (array) $terms as $term ) { 
  1785. $term_taxonomy = $taxonomy; 
  1786. if ( empty($term_taxonomy) ) 
  1787. $term_taxonomy = $term->taxonomy; 
  1788.  
  1789. wp_cache_add($term->term_id, $term, $term_taxonomy); 
  1790. wp_cache_add($term->term_taxonomy_id, $term->term_id, "$term_taxonomy:tt_id"); 
  1791.  
  1792. // 
  1793.   // Private 
  1794. // 
  1795.   
  1796. /** 
  1797. * Retrieves children of taxonomy as Term IDs. 
  1798. * 
  1799. * @package WordPress 
  1800. * @subpackage Taxonomy 
  1801. * @access private 
  1802. * @since 2.3.0 
  1803. * 
  1804. * @uses backpress_update_option() Stores all of the children in "$taxonomy_children" 
  1805. * option. That is the name of the taxonomy, immediately followed by '_children'. 
  1806. * 
  1807. * @param string $taxonomy Taxonomy Name 
  1808. * @return array Empty if $taxonomy isn't hierarachical or returns children as Term IDs. 
  1809. */ 
  1810. function _get_term_hierarchy($taxonomy) { 
  1811. if ( !$this->is_taxonomy_hierarchical($taxonomy) ) 
  1812. return array(); 
  1813. $children = $this->get_children_cache($taxonomy); 
  1814. if ( is_array($children) ) 
  1815. return $children; 
  1816.  
  1817. $children = array(); 
  1818. $terms = $this->get_terms($taxonomy, 'get=all'); 
  1819. foreach ( $terms as $term ) { 
  1820. if ( $term->parent > 0 ) 
  1821. $children[$term->parent][] = $term->term_id; 
  1822. $this->set_children_cache($taxonomy, $children); 
  1823.  
  1824. return $children; 
  1825.  
  1826. /** 
  1827. * Get the subset of $terms that are descendants of $term_id. 
  1828. * 
  1829. * If $terms is an array of objects, then _get_term_children returns an array of objects. 
  1830. * If $terms is an array of IDs, then _get_term_children returns an array of IDs. 
  1831. * 
  1832. * @package WordPress 
  1833. * @subpackage Taxonomy 
  1834. * @access private 
  1835. * @since 2.3.0 
  1836. * 
  1837. * @param int $term_id The ancestor term: all returned terms should be descendants of $term_id. 
  1838. * @param array $terms The set of terms---either an array of term objects or term IDs---from which those that are descendants of $term_id will be chosen. 
  1839. * @param string $taxonomy The taxonomy which determines the hierarchy of the terms. 
  1840. * @return array The subset of $terms that are descendants of $term_id. 
  1841. */ 
  1842. function &_get_term_children($term_id, $terms, $taxonomy) { 
  1843. $empty_array = array(); 
  1844. if ( empty($terms) ) 
  1845. return $empty_array; 
  1846.  
  1847. $term_list = array(); 
  1848. $has_children = $this->_get_term_hierarchy($taxonomy); 
  1849.  
  1850. if ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) ) 
  1851. return $empty_array; 
  1852.  
  1853. foreach ( (array) $terms as $term ) { 
  1854. $use_id = false; 
  1855. if ( !is_object($term) ) { 
  1856. $term = $this->get_term($term, $taxonomy); 
  1857. if ( is_wp_error( $term ) ) 
  1858. return $term; 
  1859. $use_id = true; 
  1860.  
  1861. if ( $term->term_id == $term_id ) 
  1862. continue; 
  1863.  
  1864. if ( $term->parent == $term_id ) { 
  1865. if ( $use_id ) 
  1866. $term_list[] = $term->term_id; 
  1867. else 
  1868. $term_list[] = $term; 
  1869.  
  1870. if ( !isset($has_children[$term->term_id]) ) 
  1871. continue; 
  1872.  
  1873. if ( $children = $this->_get_term_children($term->term_id, $terms, $taxonomy) ) 
  1874. $term_list = array_merge($term_list, $children); 
  1875.  
  1876. return $term_list; 
  1877.  
  1878. /** 
  1879. * Add count of children to parent count. 
  1880. * 
  1881. * Recalculates term counts by including items from child terms. Assumes all 
  1882. * relevant children are already in the $terms argument. 
  1883. * 
  1884. * @package WordPress 
  1885. * @subpackage Taxonomy 
  1886. * @access private 
  1887. * @since 2.3 
  1888. * 
  1889. * @param array $terms List of Term IDs 
  1890. * @param string $taxonomy Term Context 
  1891. * @return null Will break from function if conditions are not met. 
  1892. */ 
  1893. function _pad_term_counts(&$terms, $taxonomy) { 
  1894. return; 
  1895.  
  1896. /** 
  1897. * Determine if the given object is associated with any of the given terms. 
  1898. * 
  1899. * The given terms are checked against the object's terms' term_ids, names and slugs. 
  1900. * Terms given as integers will only be checked against the object's terms' term_ids. 
  1901. * If no terms are given, determines if object is associated with any terms in the given taxonomy. 
  1902. * 
  1903. * @since 2.7.0 
  1904. * @uses WP_Taxonomy::get_object_term_cache() 
  1905. * @uses WP_Taxonomy::get_object_terms() 
  1906. * 
  1907. * @param int $object_id. ID of the object (post ID, link ID, ...) 
  1908. * @param string $taxonomy. Single taxonomy name 
  1909. * @param int|string|array $terms Optional. Term term_id, name, slug or array of said 
  1910. * @return bool|WP_Error. WP_Error on input error. 
  1911. */ 
  1912. function is_object_in_term( $object_id, $taxonomy, $terms = null ) { 
  1913. if ( !$object_id = (int) $object_id ) 
  1914. return new WP_Error( 'invalid_object', __( 'Invalid object ID' ) ); 
  1915.  
  1916. $object_terms = $this->get_object_term_cache( $object_id, $taxonomy ); 
  1917. if ( empty( $object_terms ) ) 
  1918. $object_terms = $this->get_object_terms( $object_id, $taxonomy ); 
  1919.  
  1920. if ( is_wp_error( $object_terms ) ) 
  1921. return $object_terms; 
  1922. if ( empty( $object_terms ) ) 
  1923. return false; 
  1924. if ( empty( $terms ) ) 
  1925. return ( !empty( $object_terms ) ); 
  1926.  
  1927. $terms = (array) $terms; 
  1928.  
  1929. if ( $ints = array_filter( $terms, 'is_int' ) ) 
  1930. $strs = array_diff( $terms, $ints ); 
  1931. else 
  1932. $strs =& $terms; 
  1933.  
  1934. foreach ( $object_terms as $object_term ) { 
  1935. if ( $ints && in_array( $object_term->term_id, $ints ) ) return true; // If int, check against term_id 
  1936. if ( $strs ) { 
  1937. if ( in_array( $object_term->term_id, $strs ) ) return true; 
  1938. if ( in_array( $object_term->name, $strs ) ) return true; 
  1939. if ( in_array( $object_term->slug, $strs ) ) return true; 
  1940.  
  1941. return false; 
  1942.  
  1943. function get_children_cache( $taxonomy ) { return false; } 
  1944. function set_children_cache( $taxonomy, $children ) {} 
  1945. function delete_children_cache( $taxonomy ) {} 
.